DisOrder 5.0
authorRichard Kettlewell <rjk@greenend.org.uk>
Sun, 6 Jun 2010 12:36:57 +0000 (13:36 +0100)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sun, 6 Jun 2010 12:36:57 +0000 (13:36 +0100)
289 files changed:
.bzrignore
AUTHORS [new file with mode: 0644]
CHANGES.html
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Doxyfile
Makefile.am
NEWS [new file with mode: 0644]
README
README.developers
README.raw
README.reload [new file with mode: 0644]
README.upgrades
TODO [deleted file]
autogen.sh [moved from prepare with 85% similarity]
cgi/Makefile.am
cgi/actions.c
cgi/cgimain.c
cgi/disorder-cgi.h
cgi/login.c
cgi/macros-disorder.c
cgi/options.c
clients/Makefile.am
clients/authorize.c
clients/disorder.c
clients/disorderfm.c
clients/playrtp-alsa.c [deleted file]
clients/playrtp-coreaudio.c [deleted file]
clients/playrtp-oss.c [deleted file]
clients/playrtp.c
clients/playrtp.h
clients/resample.c [new file with mode: 0644]
clients/rtpmon.c [new file with mode: 0644]
configure.ac
debian/Makefile.am
debian/changelog
debian/control
debian/copyright
debian/disobedience-manual [new file with mode: 0644]
debian/overrides.disorder-server [new file with mode: 0644]
debian/postinst.disobedience
debian/postinst.disorder-server
debian/postrm.disobedience
debian/postrm.disorder-server
debian/rules
disobedience/Makefile.am
disobedience/added.c
disobedience/autoscroll.c [new file with mode: 0644]
disobedience/autoscroll.h [new file with mode: 0644]
disobedience/choose-menu.c
disobedience/choose-search.c
disobedience/choose.c
disobedience/client.c
disobedience/control.c
disobedience/disobedience.c
disobedience/disobedience.h
disobedience/help.c
disobedience/log.c
disobedience/login.c
disobedience/manual/Makefile.am [new file with mode: 0644]
disobedience/manual/arch-simple.png [new file with mode: 0644]
disobedience/manual/arch-simple.svg [new file with mode: 0644]
disobedience/manual/button-pause.png [new file with mode: 0644]
disobedience/manual/button-playing.png [new file with mode: 0644]
disobedience/manual/button-random.png [new file with mode: 0644]
disobedience/manual/button-rtp.png [new file with mode: 0644]
disobedience/manual/button-scratch.png [new file with mode: 0644]
disobedience/manual/choose-search.png [new file with mode: 0644]
disobedience/manual/choose.png [new file with mode: 0644]
disobedience/manual/disobedience-debian-menu.png [new file with mode: 0644]
disobedience/manual/disobedience-terminal.png [new file with mode: 0644]
disobedience/manual/disobedience.css [new file with mode: 0644]
disobedience/manual/disorder-email-confirm.png [new file with mode: 0644]
disobedience/manual/disorder-web-login.png [new file with mode: 0644]
disobedience/manual/index.html [new file with mode: 0644]
disobedience/manual/intro.html [new file with mode: 0644]
disobedience/manual/login.png [new file with mode: 0644]
disobedience/manual/menu-control.png [new file with mode: 0644]
disobedience/manual/menu-edit.png [new file with mode: 0644]
disobedience/manual/menu-help.png [new file with mode: 0644]
disobedience/manual/menu-server.png [new file with mode: 0644]
disobedience/manual/misc.html [new file with mode: 0644]
disobedience/manual/playlist-create.png [new file with mode: 0644]
disobedience/manual/playlist-picker-menu.png [new file with mode: 0644]
disobedience/manual/playlist-popup-menu.png [new file with mode: 0644]
disobedience/manual/playlist-window.png [new file with mode: 0644]
disobedience/manual/playlists.html [new file with mode: 0644]
disobedience/manual/properties.html [new file with mode: 0644]
disobedience/manual/queue-menu.png [new file with mode: 0644]
disobedience/manual/queue.png [new file with mode: 0644]
disobedience/manual/queue2.png [new file with mode: 0644]
disobedience/manual/recent.png [new file with mode: 0644]
disobedience/manual/tabs.html [new file with mode: 0644]
disobedience/manual/track-properties.png [new file with mode: 0644]
disobedience/manual/volume-slider.png [new file with mode: 0644]
disobedience/manual/window.html [new file with mode: 0644]
disobedience/menu.c
disobedience/misc.c
disobedience/multidrag.c [new file with mode: 0644]
disobedience/multidrag.h [new file with mode: 0644]
disobedience/playlists.c [new file with mode: 0644]
disobedience/popup.c
disobedience/popup.h
disobedience/progress.c
disobedience/properties.c
disobedience/queue-generic.c
disobedience/queue-generic.h
disobedience/queue-menu.c
disobedience/queue.c
disobedience/recent.c
disobedience/rtp.c
disobedience/settings.c
disobedience/users.c
doc/Makefile.am
doc/disobedience.1.in
doc/disorder-playrtp.1.in
doc/disorder.1.in
doc/disorder.3
doc/disorder_config.5.in
doc/disorder_protocol.5.in
doc/disorderd.8.in
driver/Makefile.am [deleted file]
driver/disorder.c [deleted file]
images/Makefile.am
images/cards-simple-fanned.svg [new file with mode: 0644]
images/cards-thin.svg [new file with mode: 0644]
images/cards24.png [new file with mode: 0644]
images/cards48.png [new file with mode: 0644]
images/duck55.png [new file with mode: 0644]
lib/Makefile.am
lib/addr.c
lib/addr.h
lib/alsabg.c [deleted file]
lib/alsabg.h [deleted file]
lib/asprintf.c
lib/authhash.c
lib/basen.c
lib/basen.h
lib/byte-order.h [new file with mode: 0644]
lib/cache.c
lib/cgi.c
lib/charset.c
lib/client-common.c
lib/client.c
lib/client.h
lib/configuration.c
lib/configuration.h
lib/cookies.c
lib/coreaudio.c [new file with mode: 0644]
lib/coreaudio.h [moved from lib/mem-impl.h with 64% similarity]
lib/dateparse.c
lib/defs.c
lib/defs.h
lib/eclient.c
lib/eclient.h
lib/event.c
lib/event.h
lib/fprintf.c
lib/hash.c
lib/hash.h
lib/hex.c
lib/hostname.c
lib/ifreq.c
lib/inputline.c
lib/kvp.c
lib/log-impl.h [deleted file]
lib/log.c
lib/log.h
lib/logfd.c
lib/macros-builtin.c
lib/macros.c
lib/mem.c
lib/mem.h
lib/mime.c
lib/mixer-alsa.c [deleted file]
lib/mixer-oss.c [deleted file]
lib/mixer.c [deleted file]
lib/mixer.h [deleted file]
lib/printf.c
lib/queue.c
lib/queue.h
lib/random.c
lib/regsub.c
lib/resample.c [new file with mode: 0644]
lib/resample.h [new file with mode: 0644]
lib/rights.c
lib/sendmail.c
lib/sink.c
lib/speaker-protocol.c
lib/speaker-protocol.h
lib/syscalls.c
lib/syscalls.h
lib/timeval.h
lib/trackdb-int.h
lib/trackdb-playlists.c [new file with mode: 0644]
lib/trackdb-stub.c
lib/trackdb.c
lib/trackdb.h
lib/trackname.c
lib/trackname.h
lib/trackorder.c
lib/tracksort.c
lib/uaudio-alsa.c [new file with mode: 0644]
lib/uaudio-apis.c [new file with mode: 0644]
lib/uaudio-command.c [new file with mode: 0644]
lib/uaudio-coreaudio.c [new file with mode: 0644]
lib/uaudio-oss.c [new file with mode: 0644]
lib/uaudio-rtp.c [new file with mode: 0644]
lib/uaudio-schedule.c [new file with mode: 0644]
lib/uaudio-thread.c [new file with mode: 0644]
lib/uaudio.c [new file with mode: 0644]
lib/uaudio.h [new file with mode: 0644]
lib/unicode.c
lib/unidata.c
lib/unidata.h
lib/url.c
lib/user.c
lib/validity.c [new file with mode: 0644]
lib/validity.h [new file with mode: 0644]
lib/wstat.c
lib/wstat.h
libtests/Makefile.am
libtests/t-addr.c
libtests/t-basen.c
libtests/t-cgi.c
libtests/t-configuration.c [moved from server/api-client.h with 79% similarity]
libtests/t-dateparse.c
libtests/t-event.c
libtests/t-resample.c [new file with mode: 0644]
libtests/t-unicode.c
libtests/test.c
plugins/Makefile.am
python/disorder.py.in
scripts/completion.bash
scripts/dist
scripts/htmlman
scripts/make-unidata
scripts/sedfiles.make
server/Makefile.am
server/api-client.c [deleted file]
server/api.c
server/background.c [new file with mode: 0644]
server/choose.c
server/daemonize.c
server/dbparams.c [new file with mode: 0644]
server/dbupgrade.c
server/deadlock.c
server/decode.c
server/disorder-server.h
server/disorderd.c
server/dump.c
server/endian.c [new file with mode: 0644]
server/normalize.c
server/play.c
server/plugin.c
server/queue-ops.c
server/rescan.c
server/schedule.c
server/server-queue.c
server/server.c
server/speaker-alsa.c [deleted file]
server/speaker-command.c [deleted file]
server/speaker-coreaudio.c [deleted file]
server/speaker-network.c [deleted file]
server/speaker-oss.c [deleted file]
server/speaker.c
server/speaker.h [deleted file]
server/state.c
server/stats.c
server/test-config [new file with mode: 0644]
server/trackname.c
sounds/Makefile.am
sounds/long.ogg [new file with mode: 0644]
sounds/scratch-mp3.raw [new file with mode: 0644]
sounds/scratch.flac [new file with mode: 0644]
sounds/scratch.mp3 [new file with mode: 0644]
sounds/scratch.raw [new file with mode: 0644]
sounds/scratch.wav [new file with mode: 0644]
templates/Makefile.am
templates/about.tmpl
templates/macros.tmpl
tests/Makefile.am
tests/dtest.py
tests/fail.py [new file with mode: 0755]
tests/hashes.py [new file with mode: 0755]
tests/play.py
tests/playlists.py [new file with mode: 0755]
tests/schedule.py
tests/udplog.c

index 022ddca..150c85c 100644 (file)
@@ -107,7 +107,7 @@ doc/disorder-normalize.8.html
 doc/disorder-decode.8.html
 doc/disorder-decode.8
 doc/plumbing.png
-disobedience/images.h
+images/images.h
 debian/disorder-server
 doc/disorder-stats.8
 doc/disorder-stats.8.html
@@ -143,8 +143,6 @@ lib/versionstring.h
 scripts/setup
 examples/disorder.rc
 scripts/teardown
-sounds/long.ogg
-sounds/slap.raw
 server/disorder-choose
 libtests/t-addr
 libtests/t-basen
@@ -174,6 +172,7 @@ libtests/t-words
 libtests/t-wstat
 libtests/t-macros
 libtests/t-cgi
+libtests/t-configuration
 doc/*.tmpl
 doc/disorder_templates.5
 oc/disorder_templates.5.html
@@ -200,3 +199,9 @@ doc/disorder_preferences.5.html
 plugins/index.html
 doc/disorder-choose.8
 doc/disorder-choose.8.html
+config.aux/compile
+server/endian
+clients/rtpmon
+libtests/t-resample
+clients/resample
+disobedience/manual/Makefile
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..57c71b4
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+See the end of README for authorship details.
index 6dca25c..ed1e9b4 100644 (file)
@@ -39,11 +39,21 @@ h4 {
 }
 
 table.bugs {
-  width: 100%
+  width: 100%;
+  font-size: 12pt;
+  border-collapse: collapse;
+  border:1px
 }
 
 table.bugs th {
-  text-align: left
+  text-align: left;
+  border: 1px solid black;
+  background-color: black;
+  color: white
+}
+
+table.bugs td {
+  border: 1px solid
 }
 
 span.command {
@@ -58,6 +68,206 @@ span.command {
 <p>This file documents recent user-visible changes to <a
  href="http://www.greenend.org.uk/rjk/disorder/">DisOrder</a>.</p>
 
+<h2>Changes up to version 5.0</h2>
+
+  <div class=section>
+  
+    <h3>Server</h3>
+    <div class=section>
+
+      <p>The <tt>device</tt> configuration option now works under OS X.
+      Devices may
+      be specified either by UID or name.  Fixes <a
+       href="http://code.google.com/p/disorder/issues/detail?id=27">Issue
+      27</a>.</p>
+
+      <p>Gapless play should be more reliable, and playback latency over RTP
+      should be a bit lower.  Note though that all the sound output code has
+      been reorganized and in some cases completely rewritten, so it's possible
+      that bugs may have been (re-)introduced.  Decoding of scratches is also
+      initiated ahead of time, giving more reliable playback.</p>
+      
+      <p>The <tt>command</tt> backend now (optionally) sends silence instead
+      of suspending writes when a pause occurs or no track is playing.</p>
+
+      <p>If <a href="http://www.mega-nerd.com/SRC/">libsamplerate</a> is
+      available at build time then that will be used instead of invoking
+      <a href="http://sox.sourceforge.net/">SoX</a>.  SoX support will be
+      removed in a future version.</p>
+
+      <p>The libao plugin has been removed, because the plugin API is not
+      usable in libao 1.0.0.</p>
+
+      <p>Playlists are now supported.  These allow a collection of tracks to be
+      prepared offline and played as a unit.</p>
+
+    </div>
+      
+    <h3>Disobedience</h3>
+
+    <div class=section>
+
+      <p>Multiple tracks can now be dragged in the queue in a single operation.
+      Furthermore, it is now possible to drag tracks from the
+      &ldquo;Recent&rdquo;, &ldquo;Added&rdquo; and &ldquo;Choose&rdquo; tabs
+      to the queue.</p>
+
+      <p>Disobedience now supports playlist editing and has a compact mode,
+      available from the <b>Control</b> menu.</p>
+
+      <p>Disobedience has a <a href="disobedience/manual/index.html">new
+      manual</a>.</p>
+
+    </div>
+
+    <h3>Web Interface</h3>
+    <div class=section>
+
+      <p>Confirmation URLs should be cleaner (and in particular not end
+      with punctuation).  (Please see <a
+      href="README.upgrades">README.upgrades</a> for more about this.)</p>
+
+    </div>
+      
+    <h3>RTP Player</h3>
+  
+    <div class=section>
+
+      <p>There is a new <tt>--command</tt> option which allows the RTP player
+      to send audio data to a user-chosen command instead of an audio API. See
+      the man page for details.</p>
+      
+      <p>The <tt>--device</tt> option to <tt>disorder-playrtp</tt> now works
+      under OS X (as above).</p>
+        
+    </div>
+      
+    <h3>General</h3>
+
+    <div class=section>
+
+      <p>IPv6 should now work.  Network address specifications
+      (<tt>listen</tt>, <tt>connect</tt> etc) can now be prefixed with
+      <tt>-4</tt> or <tt>-6</tt> to force use
+      of IPv4 or IPv6, though normally this should not be needed.</p>
+
+      <p>Unicode support has been upgraded to <a
+      href="http://www.unicode.org/versions/Unicode5.1.0/">Unicode
+      5.1.0</a>.</p>
+
+      <p>Various elements of the source code have been
+      rationalized.</p>
+      
+    </div>
+    
+    <h3>Bugs fixed</h3>
+  
+    <div class=section>
+
+      <table class=bugs>
+        <tr>
+          <th>ID</th>
+          <th>Description</th>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=22">#22</a></td>
+          <td>Background decoders interact badly with server reload</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=27">#27</a></td>
+          <td>Mac DisOrder uses wrong sound device</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=30">#30</a></d>
+          <td>mini disobedience interface</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=32">#32</a></d>
+          <td>Excessively verbose log chatter on shutdown</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=33">#33</a></d>
+          <td>(Some) plugins need -lm.</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=39">#39</a></d>
+          <td>Double bind() non-multicast AF_INET</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=40">#40</a></d>
+          <td>Missing stub function</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=41">#41</a></d>
+          <td>Missing includes for timeval</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=42">#42</a></d>
+          <td>syntax error in empeg_host section</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=43">#43</a></d>
+          <td>decoder segfault with FLAC 1.2.1</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=44">#44</a></d>
+          <td>gcc 4.3.2-1ubuntu12 SUYB patch</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=45">#45</a></d>
+          <td>disobedience doesn't configure its back end</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=46">#46</a></d>
+          <td>Sort search results in web interface</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=48">#48</a></d>
+          <td>build-time dependency on <tt>oggdec</tt> removed</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=49">#49</a></d>
+          <td>Disobedience's 'When' column gets out of date</td>
+        </tr>
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=51">#51</a></td>
+          <td>Improved speaker process robustness</td>
+        </tr>
+
+        <tr>
+          <td>(none)</td>
+         <td>&ldquo;found track in no collection&rdquo; messages for scratches
+         are now suppressed</td>
+        </tr>
+
+        <tr>
+          <td>(none)</td>
+          <td>Disobedience would sometimes fail to notice when a track
+          started, leading to its display getting out of date.</td>
+        </tr>
+
+      </table>
+    </div>
+  </div>
+
 <h2>Changes up to version 4.3</h2>
 
   <div class=section>
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..4432540
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,676 @@
+
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                      TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+  
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..c263762
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,2 @@
+See version control history for detailed change information.
+       
index c603807..b350738 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -53,16 +53,6 @@ CREATE_SUBDIRS         = NO
 
 OUTPUT_LANGUAGE        = English
 
-# This tag can be used to specify the encoding used in the generated output. 
-# The encoding is not always determined by the language that is chosen, 
-# but also whether or not the output is meant for Windows or non-Windows users. 
-# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
-# forces the Windows encoding (this is the default for the Windows binary), 
-# whereas setting the tag to NO uses a Unix-style encoding (the default for 
-# all platforms other than Windows).
-
-USE_WINDOWS_ENCODING   = NO
-
 # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
 # include brief member descriptions after the members that are listed in 
 # the file and class documentation (similar to JavaDoc). 
@@ -1187,22 +1177,6 @@ DOT_PATH               =
 
 DOTFILE_DIRS           = 
 
-# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
-# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
-# this value, doxygen will try to truncate the graph, so that it fits within 
-# the specified constraint. Beware that most browsers cannot cope with very 
-# large images.
-
-MAX_DOT_GRAPH_WIDTH    = 1024
-
-# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
-# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
-# this value, doxygen will try to truncate the graph, so that it fits within 
-# the specified constraint. Beware that most browsers cannot cope with very 
-# large images.
-
-MAX_DOT_GRAPH_HEIGHT   = 1024
-
 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
 # graphs generated by dot. A depth value of 3 means that only nodes reachable 
 # from the root by following a path via at most 3 edges will be shown. Nodes 
index 501f344..b41014f 100644 (file)
@@ -16,7 +16,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-EXTRA_DIST=TODO CHANGES.html README.streams BUGS \
+EXTRA_DIST=CHANGES.html README.streams BUGS \
 README.upgrades README.client README.raw README.vhost README.developers
 SUBDIRS=@subdirs@
 
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..28889c4
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,3 @@
+See CHANGES.html for high-level change information.
+
+See README.upgrades for upgrade information.
diff --git a/README b/README
index a7736da..b1ddc92 100644 (file)
--- a/README
+++ b/README
@@ -24,22 +24,23 @@ It could probably be ported to some other UNIX variants without too much
 effort.
 
 Build dependencies:
-  Name             Tested              Notes
-  libdb            4.3.29              not 4.2/4.6; 4.[457] seem to be ok
+  Name             Tested      Notes
+  libdb            4.5.20      not 4.6; 4.[78] seem to be ok
   libgc            6.8
-  libvorbisfile    1.1.2
-  libpcre          6.7                 need UTF-8 support
+  libvorbisfile    1.2.0
+  libpcre          7.6         need UTF-8 support
   libmad           0.15.1b
-  libgcrypt        1.2.3
-  libao            0.8.6
-  libasound        1.0.13
-  libFLAC          1.1.2
-  GNU C            4.1.2               }
-  GNU Make         3.81                } Non-GNU versions will NOT work
-  GNU Sed          4.1.5               }
-  Python           2.4.4               (optional)
-  GTK+             2.8.20              (if you want the GTK+ client)
-  GLIB             2.12.4              (if you want the GTK+ client)
+  libgcrypt        1.4.1
+  libao            0.8.8       1.0.0 is broken
+  libasound        1.0.16
+  libFLAC          1.2.1
+  libsamplerate    0.1.4       currently optional
+  GNU C            4.2.1       }
+  GNU Make         3.81        } Non-GNU versions will NOT work
+  GNU Sed          4.1.5       }
+  Python           2.5.2       (optional; 2.4 won't work)
+  GTK+             2.12.12     (for the GTK+ client; 2.10 & older will NOT work)
+  GLIB             2.16.6      (for the GTK+ client)
 
 "Tested" means I've built against that version; earlier or later versions will
 often work too.
@@ -263,13 +264,14 @@ Copyright
   "Nothing but another drug, a licence that you buy and sell"
 
 DisOrder - select and play digital audio files
-Copyright (C) 2003-2008 Richard Kettlewell
+Copyright (C) 2003-2009 Richard Kettlewell
 Portions copyright (C) 2007 Ross Younger
 Portions copyright (C) 2007 Mark Wooding
 Portions extracted from MPG321, http://mpg321.sourceforge.net/
   Copyright (C) 2001 Joe Drew
   Copyright (C) 2000-2001 Robert Leslie
 Portions Copyright (C) 1997-2006 Free Software Foundation, Inc.
+Portions Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
 Binaries may derive extra copyright owners through linkage (binary distributors
 are expected to do their own legwork)
 
index d6f6f91..5c71b07 100644 (file)
@@ -5,39 +5,35 @@ Dependencies:
      Autoconf         2.61           Slightly older might work too
      Libtool          1.5.22         1.4 is no good
      Bazaar (bzr)                    You might be able to manage without
-     Python           2.4
+     Python           2.5.2          2.4 won't work
 
    * On Debian and derivatives this should work:
 
      apt-get install gcc libc-dev automake autoconf libtool libgtk2.0-dev \
                     libgc-dev libgcrypt-dev libpcre3-dev libvorbis-dev \
                     libao-dev libmad0-dev libasound2-dev libdb4.3-dev \
-                    libflac-dev vorbis-tools wget
+                    libflac-dev vorbis-tools wget libsamplerate0-dev
 
-     (Use the bzr from backports, the one in etch is obsolete.)
+     On lenny use libdb4.5-deb.  libdb4.6 does not work (and configure will
+     refuse to use it).
 
    * On FreeBSD you'll need at least these packages:
-        autotools
-        bash
-        flac
-        mad
-         boehm-gc
-         db43
-         gmake
-         gsed
-         libao
-         libgcrypt
-         wget
-         vorbis-tools
+       autotools bash flac mad boehm-gc db43 gmake gsed libao libgcrypt wget
+       vorbis-tools
+
+   * On OS X with Fink:
+
+     fink install gtk+2-dev gc libgrypt pcre flac vorbis-tools libmad wget \
+                  sed libsamplerate0-dev
 
    * Please report unstated dependencies (here, README or debian/control).
 
 Building:
 
-   * Compiled versions of configure and the makefiles are including in bzr, so
-     if you didn't use a source tarball, you must start as follows:
+   * Compiled versions of configure and the makefiles are not included in bzr,
+     so if you didn't use a source tarball, you must start as follows:
 
-        bash ./prepare
+        bash ./autogen.sh
         ./configure -C
         make
 
@@ -58,16 +54,11 @@ APIs And Formats:
 
    * To support a new sound API:
      1) Teach configure.ac how to detect any libraries required.
-     2) Define a new BACKEND_ value and update configuration.[ch] for it.
-     3) Create a suitable server/speaker-*.c along the pattern of the existing
-        ones.
-     4) If possible create a suitable lib/mixer-*.c.  This doesn't make sense
-        for all APIs (e.g. network), but even for those it does, playback
-        support without volume control support is likely to be acceptable (even
-        if inferior to full support).
+     2) Create lib/uaudio-<name>.c; see uaudio.h for the interface.
+     3) Update the list in lib/uaudio-apis.c
+     4) Add a new option to clients/playrtp.c and document it in
+        doc/disorder-playrtp.1.in (if appropriate).
      5) Update doc/disorder_config.5.in.
-     6) If relevant, create a suitable clients/playrtp-*.c and update
-        doc/disorder-playrtp.1.in.
 
    * To support a new file format:
      1) Teach configure.ac how to detect any libraries required.
@@ -137,8 +128,9 @@ The Server:
 Web Interface:
 
    * The web interface does not use Javascript or Flash and I would like to
-     keep it that way.  Clever use of CSS is OK provided it works well on the
-     mainstream browsers.
+     keep it that way; Javascript might be acceptable but it must degrade
+     gracefuly if disabled.  Clever use of CSS is OK provided it works well on
+     the mainstream browsers.
 
    * Update templates/help.tmpl for any changes you make.
 
index a2da623..a107964 100644 (file)
@@ -13,22 +13,8 @@ The purpose of raw format players is:
 
 ** Usage
 
-To use raw format, use the execraw module and make the command choose the
-"disorder" libao driver.  You may need to link the driver from wherever
-DisOrder installs it (e.g. /usr/local/lib/ao/plugins-2) to where libao will
-look for it (e.g. /usr/lib/ao/plugins-2 or /sw/lib/ao/plugins-2).
-
-You should pass the "fragile" option to ogg123.  This is because ogg123 ignores
-write errors!
-
-mpg321 does not appear to have this bug.
-
-For _non_ raw players it is advisable to use the new --wait-for-device option.
-This repeatedly tries to open the audio device before starting the player
-proper.  It times out after a couple of seconds.
-
-See disorder_config(5) and the example configuration file for further
-information and examples.
+By default, built-in raw-format players are used for several encodings, so you
+do not need to do anything.
 
 ** Low-Level Details
 
diff --git a/README.reload b/README.reload
new file mode 100644 (file)
index 0000000..f9fa4c7
--- /dev/null
@@ -0,0 +1,187 @@
+The effect of "disorder reconfigure"
+====================================
+
+This is current rather vaguely defined and implemented.  This file
+will lay out what you can and cannot change over a reconfigure.  Any
+other changes will require a full server restart.
+
+The situation is gradually improving; this file tracks the current
+state.
+
+* Options that might have to remain the same across restart
+
+Arguably if there is anything in this section then that's a serious
+bug!
+
+** alias (enforced at reload only)
+
+This defines how aliases are inserted into the track database.  Need
+to think about how changing it will affect things.
+
+** namepart (enforced at reload only)
+
+Probably affects alias construction.
+
+** stopword (enforced at reload only)
+
+The search database will have to be rebuilt from scratch.
+
+** user (enforced at reload only)
+
+All the files will be owned by the wrong user!
+
+** Alias, search database, etc
+
+Rescan can regenerate aliases and the search and tag databases but we
+rather assume that they are either empty or good.  Therefore we need
+to store anything that can affect these values and erase them if they
+change.
+
+The solution is a global pref _dbparams which contains the hash of the
+alias, stopword and namepart data.
+
+* Options that must remain the same across reload
+
+Some things will just require a restart.  We should either enforce
+this (refusing to accept modified configurations that purport to
+change them) or explicitly ignore it.
+
+** home (enforced at reload)
+
+We absolutely cannot accept changing our state directory.
+
+** lock (generates a deprecation warning)
+
+Liable to be removed anyway.
+
+** nice_speaker (generates a warning)
+
+You can't renice a running speaker to make it less nice (and we don't
+try to make it more nice).
+
+* Options that ought to be changable across reload but aren't
+
+These options need some work somewhere to be changeable just by a
+reload.
+
+** api
+
+The main server will cope fine with this changing.  The speaker will
+ignore the change however.
+
+** broadcast
+
+The speaker will ignore the change.
+
+** broadcast_from
+
+The speaker will ignore the change.
+
+** collection
+
+If the set of collections change we ought to initiate a rescan.
+
+** device
+
+The speaker will ignore the change.
+
+** multicast_loop
+
+The speaker will ignore the change.
+
+** multicast_ttl
+
+The speaker will ignore the change.
+
+** pause_mode
+
+The speaker will ignore the change.
+
+** sample_format
+
+The speaker will ignore the change.
+
+** speaker_command
+
+The speaker will ignore the change.
+
+* Options that can be changed across reload
+
+These options can be changed at reload and it should just work.
+
+** authorization_algoritm
+
+** channel
+
+** default_rights
+
+** listen
+
+** mixer
+
+** new_bias
+
+** new_bias_age
+
+** new_max
+
+** nice_rescan
+
+Wouldn't affect an already-running rescan, but reload already cancels
+and restarts the underway rescan anyway.
+
+** noticed_history
+
+** reminder_interval
+
+** remote_userman
+
+** replay_min
+
+** signal
+
+** sox_generation
+
+** scratch
+
+* Options that can change, but with a caveat
+
+These options can be changed at reload but there is some caveat about
+this (which ought to be documented, and in some cases is).
+
+** plugins
+
+Plugin path.  You can change the plugin path but an already-loaded
+plugin may stay loaded.
+
+** cookie_key_lifetime
+
+Only affects subsequently generated keys - cannot shorten (or extend)
+the lifetime of the current key.
+
+** cookie_login_lifetime
+
+Only affects subsequently generated cookies - cannot shorten (or
+extend) the lifetime of already-generated cookies.
+
+** history
+
+The history might not shorten until it's next written.
+
+** player
+
+Won't affect running players or decoders.
+
+** queue_pad
+
+Won't shrink the queue.
+
+** tracklength
+
+Won't affect already-computed lengths.
+
+* Implementation Considerations
+
+A likely change is that the speaker will be created on demand and
+stopped when idle.  Some changes will still be handled via SM_RELOAD
+but others may require the speaker to quit and restart.
index c586dd8..1cb6aba 100644 (file)
@@ -17,6 +17,45 @@ all 1.1.x versions.
 
 If you install from .deb files then much of this work is automated.
 
+* 4.x -> 5.0
+
+** Web Confirmation Strings
+
+The syntax of confirmation strings for online registrations has changed and old
+ones no longer work.  This only affects users who registered before the upgrade
+but have not yet confirmed their login.  You can delete such half-created users
+with 'disorder deluser USERNAME' (as an administrative user, for instance as
+root on the server) and they can start the registration process again.
+
+** Handling Of Configuration Changes
+
+There is a new mechanism to ensure that the search database and aliases are
+reconstructed if any options that affect them change.  Unfortunately this means
+that the reconstruction step always takes place on upgrade from 4.3 or earlier,
+as those versions don't record sufficient information for the server to tell
+whether it needs to reconstruct or not.
+
+The result will be a log message of the form:
+
+new database parameter string dbparams-0-sha256:61609f3e6395ec8dee317ee216fe2848d70c249d347dd03c6a219441a13dd456 - removing old data
+
+...and a slower rescan on startup.  Subsequent restarts should not have this
+problem (unless of course you change a relevant option).
+
+** Deprecation Notices
+
+The player --wait-for-device option is deprecated and will be removed in a
+future version.
+
+The 'lock' option no longer does anything.  You must delete it from any
+configuration files that contain it.  The full set of deprecated options is:
+  allow
+  gap
+  lock
+  prefsync
+  restrict
+  trust
+
 * 3.0 -> 4.x
 
 If you customized any of the templates, you will pretty much have to start from
diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index e15c225..0000000
--- a/TODO
+++ /dev/null
@@ -1,25 +0,0 @@
--*-outline-*-
-
-* plugins
-
-** configuration
-
-Allow plugins to be configured via the main config file somehow.
-
-* web interface
-
-** language choice
-
-Parse HTTP_ACCEPT_LANGUAGE and use it to choose template subdirectory.
-I might leave this until I hear that someone actually wants a
-multilingual jukebox.
-
-** rearrange queue
-
-Needs thought on how to design the interface.
-
-** improve volume control
-
-** templates
-
-Build defaults into program to save file IO.
similarity index 85%
rename from prepare
rename to autogen.sh
index dd3de9b..a63049a 100755 (executable)
--- a/prepare
@@ -21,13 +21,6 @@ set -e
 srcdir=$(dirname $0)
 here=$(pwd)
 cd $srcdir
-rm -f COPYING
-for f in /usr/share/common-licenses/GPL-3 $HOME/doc/GPL-3 $HOME/Documents/GPL-3; do
-  if test -e "$f"; then
-    ln -s "$f" COPYING
-    break
-  fi
-done
 if test -d $HOME/share/aclocal; then
   aclocal --acdir=$HOME/share/aclocal
 else
index 22e2a77..88cb24d 100644 (file)
@@ -1,6 +1,6 @@
 #
 # This file is part of DisOrder.
-# Copyright (C) 2004-2008 Richard Kettlewell
+# Copyright (C) 2004-2009 Richard Kettlewell
 #
 # 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
@@ -23,7 +23,7 @@ AM_CPPFLAGS=-I${top_srcdir}/lib -I../lib
 disorder_SOURCES=macros-disorder.c lookup.c options.c actions.c        \
        login.c cgimain.c disorder-cgi.h
 disorder_LDADD=../lib/libdisorder.a \
-       $(LIBPCRE) $(LIBGCRYPT) $(LIBDL) $(LIBDB)
+       $(LIBPCRE) $(LIBGCRYPT) $(LIBDL) $(LIBDB) $(LIBICONV)
 disorder_LDFLAGS=-export-dynamic
 disorder_DEPENDENCIES=../lib/libdisorder.a
 
index d752660..fae533a 100644 (file)
@@ -44,7 +44,7 @@ static void redirect(const char *url) {
   if(printf("Location: %s\n"
             "%s\n"
             "\n", url, dcgi_cookie_header()) < 0)
-    fatal(errno, "error writing to stdout");
+    disorder_fatal(errno, "error writing to stdout");
 }
 
 /*$ playing
@@ -75,7 +75,7 @@ static void act_playing(void) {
      && length
      && dcgi_playing->sofar >= 0) {
     /* Try to put the next refresh at the start of the next track. */
-    time(&now);
+    xtime(&now);
     fin = now + length - dcgi_playing->sofar + config->gap;
     if(now + refresh > fin)
       refresh = fin - now;
@@ -95,13 +95,17 @@ static void act_playing(void) {
     if(refresh > config->gap)
       refresh = config->gap;
   }
+  /* Bound the refresh interval below as a back-stop against the above
+   * calculations coming up with a stupid answer */
+  if(refresh < config->refresh_min)
+    refresh = config->refresh_min;
   if((action = cgi_get("action")))
     url = cgi_makeurl(config->url, "action", action, (char *)0);
   else
     url = config->url;
   if(printf("Refresh: %ld;url=%s\n",
             refresh, url) < 0)
-    fatal(errno, "error writing to stdout");
+    disorder_fatal(errno, "error writing to stdout");
   dcgi_expand("playing", 1);
 }
 
@@ -176,12 +180,12 @@ static void act_remove(void) {
 
   if(dcgi_client) {
     if(!(id = cgi_get("id")))
-      error(0, "missing 'id' argument");
+      disorder_error(0, "missing 'id' argument");
     else if(!(q = dcgi_findtrack(id)))
-      error(0, "unknown queue id %s", id);
+      disorder_error(0, "unknown queue id %s", id);
     else if(q->origin == origin_scratch)
       /* can't scratch scratches */
-      error(0, "does not make sense to scratch or remove %s", id);
+      disorder_error(0, "does not make sense to scratch or remove %s", id);
     else if(q->state == playing_paused
             || q->state == playing_started)
       /* removing the playing track = scratching */
@@ -191,7 +195,7 @@ static void act_remove(void) {
       disorder_remove(dcgi_client, id);
     else
       /* various error states */
-      error(0, "does not make sense to scratch or remove %s", id);
+      disorder_error(0, "does not make sense to scratch or remove %s", id);
   }
   redirect(0);
 }
@@ -208,18 +212,18 @@ static void act_move(void) {
 
   if(dcgi_client) {
     if(!(id = cgi_get("id")))
-      error(0, "missing 'id' argument");
+      disorder_error(0, "missing 'id' argument");
     else if(!(delta = cgi_get("delta")))
-      error(0, "missing 'delta' argument");
+      disorder_error(0, "missing 'delta' argument");
     else if(!(q = dcgi_findtrack(id)))
-      error(0, "unknown queue id %s", id);
+      disorder_error(0, "unknown queue id %s", id);
     else switch(q->state) {
     case playing_random:                /* unplayed randomly chosen track */
     case playing_unplayed:              /* haven't played this track yet */
       disorder_move(dcgi_client, id, atol(delta));
       break;
     default:
-      error(0, "does not make sense to scratch %s", id);
+      disorder_error(0, "does not make sense to scratch %s", id);
       break;
     }
   }
@@ -438,7 +442,7 @@ static void act_register(void) {
                 "\n"
                 "%s?c=%s\n", config->url, urlencodestring(confirm));
   if(!(text = mime_encode_text(text, &charset, &encoding)))
-    fatal(0, "cannot encode email");
+    disorder_fatal(0, "cannot encode email");
   byte_xasprintf(&content_type, "text/plain;charset=%s",
                 quote822(charset, 0));
   sendmail("", config->mail_sender, email, "Welcome to DisOrder",
@@ -726,19 +730,19 @@ void dcgi_expand(const char *name, int header) {
     mx_expand_file(found, sink_discard(), 0);
   /* For unknown actions check that they aren't evil */
   if(!dcgi_valid_action(name))
-    fatal(0, "invalid action name '%s'", name);
+    disorder_fatal(0, "invalid action name '%s'", name);
   byte_xasprintf((char **)&p, "%s.tmpl", name);
   if(!(found = mx_find(p, 0/*report*/)))
-    fatal(errno, "cannot find %s", p);
+    disorder_fatal(errno, "cannot find %s", p);
   if(header) {
     if(printf("Content-Type: text/html; charset=UTF-8\n"
               "%s\n"
               "\n", dcgi_cookie_header()) < 0)
-      fatal(errno, "error writing to stdout");
+      disorder_fatal(errno, "error writing to stdout");
   }
   if(mx_expand_file(found, sink_stdio("stdout", stdout), 0) == -1
      || fflush(stdout) < 0)
-    fatal(errno, "error writing to stdout");
+    disorder_fatal(errno, "error writing to stdout");
 }
 
 /** @brief Execute a web action
index 583af30..4ea98ec 100644 (file)
@@ -48,7 +48,7 @@ int main(int argc, char **argv) {
   if(getenv("DISORDER_DEBUG"))
     debugging = 1;
   /* Read configuration */
-  if(config_read(0/*!server*/))
+  if(config_read(0/*!server*/, NULL))
     exit(EXIT_FAILURE);
   /* Figure out our URL.  This can still be overridden from the config file if
    * necessary but it shouldn't be necessary in ordinary installations. */
@@ -65,7 +65,7 @@ int main(int argc, char **argv) {
   mx_search_path(pkgdatadir);
   /* Never cache anythging */
   if(printf("Cache-Control: no-cache\n") < 0)
-    fatal(errno, "error writing to stdout");
+    disorder_fatal(errno, "error writing to stdout");
   /* Create the initial connection, trying the cookie if we found a suitable
    * one. */
   dcgi_login();
@@ -74,7 +74,7 @@ int main(int argc, char **argv) {
   /* In practice if a write fails that probably means the web server went away,
    * but we log it anyway. */
   if(fclose(stdout) < 0)
-    fatal(errno, "error closing stdout");
+    disorder_fatal(errno, "error closing stdout");
   return 0;
 }
 
index 9a455e9..4587e78 100644 (file)
@@ -52,6 +52,7 @@
 #include "mime.h"
 #include "sendmail.h"
 #include "charset.h"
+#include "syscalls.h"
 
 extern disorder_client *dcgi_client;
 extern char *dcgi_cookie;
index a7a6dc0..c57be06 100644 (file)
@@ -82,7 +82,7 @@ void dcgi_get_cookie(void) {
       if(best_cookie != -1)
        dcgi_cookie = cd.cookies[best_cookie].value;
     } else
-      error(0, "could not parse cookie field '%s'", cookie_env);
+      disorder_error(0, "could not parse cookie field '%s'", cookie_env);
   }
 }
 
index c0eee5d..29835bb 100644 (file)
@@ -859,24 +859,36 @@ static int exp__files_dirs(int nargs,
   /* Get the list */
   if(fn(dcgi_client, dir, re, &tracks, &ntracks))
     return 0;
-  /* Sort it.  NB trackname_transform() does not go to the server. */
-  tsd = tracksort_init(ntracks, tracks, type);
-  /* Expand the subsiduary templates.  We chuck in @sort and @display because
-   * it is particularly easy to do so. */
-  for(n = 0; n < ntracks; ++n)
-    if((rc = mx_expand(mx_rewritel(m,
-                                   "index", make_index(n),
-                                   "parity", n % 2 ? "odd" : "even",
-                                   "track", tsd[n].track,
-                                   "first", n == 0 ? "true" : "false",
-                                   "last", n + 1 == ntracks ? "false" : "true",
-                                   "sort", tsd[n].sort,
-                                   "display", tsd[n].display,
-                                   (char *)0),
-                       output, u)))
-      return rc;
+  if(type) {
+    /* Sort it.  NB trackname_transform() does not go to the server. */
+    tsd = tracksort_init(ntracks, tracks, type);
+    /* Expand the subsiduary templates.  We chuck in @sort and @display because
+     * it is particularly easy to do so. */
+    for(n = 0; n < ntracks; ++n)
+      if((rc = mx_expand(mx_rewritel(m,
+                                     "index", make_index(n),
+                                     "parity", n % 2 ? "odd" : "even",
+                                     "track", tsd[n].track,
+                                     "first", n == 0 ? "true" : "false",
+                                     "last", n + 1 == ntracks ? "false" : "true",
+                                     "sort", tsd[n].sort,
+                                     "display", tsd[n].display,
+                                     (char *)0),
+                         output, u)))
+        return rc;
+  } else {
+    for(n = 0; n < ntracks; ++n)
+      if((rc = mx_expand(mx_rewritel(m,
+                                     "index", make_index(n),
+                                     "parity", n % 2 ? "odd" : "even",
+                                     "track", tracks[n],
+                                     "first", n == 0 ? "true" : "false",
+                                     "last", n + 1 == ntracks ? "false" : "true",
+                                     (char *)0),
+                         output, u)))
+        return rc;
+  }
   return 0;
-
 }
 
 /*$ @tracks{DIR}{RE}{TEMPLATE}
@@ -936,14 +948,12 @@ static int exp__search_shim(disorder_client *c, const char *terms,
  * - @parity: "even" or "odd" alternately
  * - @first: "true" on the first directory and "false" otherwise
  * - @last: "true" on the last directory and "false" otherwise
- * - @sort: the sort key for this track
- * - @display: the UNQUOTED display string for this track
  */
 static int exp_search(int nargs,
                       const struct mx_node **args,
                       struct sink *output,
                       void *u) {
-  return exp__files_dirs(nargs, args, output, u, "track", exp__search_shim);
+  return exp__files_dirs(nargs, args, output, u, NULL, exp__search_shim);
 }
 
 /*$ @label{NAME}
@@ -978,7 +988,7 @@ static int exp_breadcrumbs(int attribute((unused)) nargs,
     return rc;
   /* Reject relative paths */
   if(dir[0] != '/') {
-    error(0, "breadcrumbs: '%s' is a relative path", dir);
+    disorder_error(0, "breadcrumbs: '%s' is a relative path", dir);
     return 0;
   }
   /* Skip the root */
index 05712c2..fca5801 100644 (file)
@@ -72,7 +72,7 @@ static void option__split_error(const char *msg,
                               void *u) {
   struct read_options_state *cs = u;
   
-  error(0, "%s:%d: %s", cs->name, cs->line, msg);
+  disorder_error(0, "%s:%d: %s", cs->name, cs->line, msg);
 }
 
 static void option__readfile(const char *name) {
@@ -84,7 +84,7 @@ static void option__readfile(const char *name) {
   if(!(cs.name = mx_find(name, 1/*report*/)))
     return;
   if(!(fp = fopen(cs.name, "r")))
-    fatal(errno, "error opening %s", cs.name);
+    disorder_fatal(errno, "error opening %s", cs.name);
   cs.line = 0;
   while(!inputline(cs.name, fp, &buffer, '\n')) {
     ++cs.line;
@@ -94,17 +94,17 @@ static void option__readfile(const char *name) {
     if(!n)
       continue;
     if((i = TABLE_FIND(options, name, vec[0])) == -1) {
-      error(0, "%s:%d: unknown option '%s'", cs.name, cs.line, vec[0]);
+      disorder_error(0, "%s:%d: unknown option '%s'", cs.name, cs.line, vec[0]);
       continue;
     }
     ++vec;
     --n;
     if(n < options[i].minargs) {
-      error(0, "%s:%d: too few arguments to '%s'", cs.name, cs.line, vec[-1]);
+      disorder_error(0, "%s:%d: too few arguments to '%s'", cs.name, cs.line, vec[-1]);
       continue;
     }
     if(n > options[i].maxargs) {
-      error(0, "%s:%d: too many arguments to '%s'", cs.name, cs.line, vec[-1]);
+      disorder_error(0, "%s:%d: too many arguments to '%s'", cs.name, cs.line, vec[-1]);
       continue;
     }
     options[i].handler(n, vec);
index 91d4448..91afc1c 100644 (file)
@@ -1,6 +1,6 @@
 #
 # This file is part of DisOrder.
-# Copyright (C) 2006-2008 Richard Kettlewell
+# Copyright (C) 2006-2009 Richard Kettlewell
 #
 # 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
@@ -17,7 +17,7 @@
 #
 
 bin_PROGRAMS=disorder disorderfm disorder-playrtp
-noinst_PROGRAMS=filename-bytes
+noinst_PROGRAMS=filename-bytes rtpmon resample
 noinst_SCRIPTS=dump2wav
 
 AM_CPPFLAGS=-I${top_srcdir}/lib -I../lib
@@ -25,7 +25,7 @@ AM_CPPFLAGS=-I${top_srcdir}/lib -I../lib
 disorder_SOURCES=disorder.c authorize.c authorize.h \
        ../lib/memgc.c
 disorder_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
-       $(LIBGC) $(LIBGCRYPT) $(LIBPCRE) $(LIBDB)
+       $(LIBGC) $(LIBGCRYPT) $(LIBPCRE) $(LIBICONV) $(LIBDB) $(LIBPTHREAD)
 disorder_DEPENDENCIES=$(LIBOBJS) ../lib/libdisorder.a
 
 disorderfm_SOURCES=disorderfm.c \
@@ -33,15 +33,20 @@ disorderfm_SOURCES=disorderfm.c \
 disorderfm_LDADD=$(LIBOBJS) ../lib/libdisorder.a $(LIBGC) $(LIBICONV)
 disorderfm_DEPENDENCIES=$(LIBOBJS) ../lib/libdisorder.a
 
-disorder_playrtp_SOURCES=playrtp.c playrtp.h playrtp-mem.c \
-                        playrtp-alsa.c playrtp-coreaudio.c playrtp-oss.c
+disorder_playrtp_SOURCES=playrtp.c playrtp.h playrtp-mem.c
 disorder_playrtp_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
        $(LIBASOUND) $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT) $(COREAUDIO) \
-       $(LIBDB) $(LIBPTHREAD)
+       $(LIBDB) $(LIBPTHREAD) -lm
 disorder_playrtp_DEPENDENCIES=$(LIBOBJS) ../lib/libdisorder.a
 
+rtpmon_SOURCES=rtpmon.c
+rtpmon_LDADD=$(LIBOBJS) ../lib/libdisorder.a
+
 filename_bytes_SOURCES=filename-bytes.c
 
+resample_SOURCES=resample.c
+resample_LDADD=$(LIBOBJS) ../lib/libdisorder.a $(LIBSAMPLERATE)
+
 install-exec-hook:
        $(LIBTOOL) --mode=finish $(DESTDIR)$(libdir)
 
index 93fe3f4..652db04 100644 (file)
@@ -56,7 +56,7 @@ int authorize(disorder_client *client, const char *user, const char *rights) {
   if(!(pw = getpwnam(user)))
     /* If it's a NIS world then /etc/passwd may be a lie, but it emphasizes
      * that it's talking about the login user, not the DisOrder user */
-    fatal(0, "no such user as %s in /etc/passwd", user);
+    disorder_fatal(0, "no such user as %s in /etc/passwd", user);
 
   /* Choose a random password */
   gcry_randomize(pwbin, sizeof pwbin, GCRY_STRONG_RANDOM);
@@ -68,46 +68,46 @@ int authorize(disorder_client *client, const char *user, const char *rights) {
 
   /* Become the target user */
   if(setegid(pw->pw_gid) < 0)
-    fatal(errno, "setegid %lu", (unsigned long)pw->pw_gid);
+    disorder_fatal(errno, "setegid %lu", (unsigned long)pw->pw_gid);
   if(seteuid(pw->pw_uid) < 0)
-    fatal(errno, "seteuid %lu", (unsigned long)pw->pw_uid);
+    disorder_fatal(errno, "seteuid %lu", (unsigned long)pw->pw_uid);
   
   /* Make sure the configuration directory exists*/
   byte_xasprintf(&configdir, "%s/.disorder", pw->pw_dir);
   if(mkdir(configdir, 02700) < 0) {
     if(errno != EEXIST)
-      fatal(errno, "creating %s", configdir);
+      disorder_fatal(errno, "creating %s", configdir);
   }
 
   /* Make sure the configuration file does not exist */
   byte_xasprintf(&configpath, "%s/passwd", configdir);
   if(lstat(configpath, &sb) == 0)
-    fatal(0, "%s already exists", configpath);
+    disorder_fatal(0, "%s already exists", configpath);
   if(errno != ENOENT)
-    fatal(errno, " checking %s", configpath);
+    disorder_fatal(errno, " checking %s", configpath);
   
   byte_xasprintf(&configpathtmp, "%s.new", configpath);
 
   /* Create config file with mode 600 */
   if((fd = open(configpathtmp, O_WRONLY|O_CREAT, 0600)) < 0)
-    fatal(errno, "error creating %s", configpathtmp);
+    disorder_fatal(errno, "error creating %s", configpathtmp);
 
   /* Write password */
   if(!(fp = fdopen(fd, "w")))
-    fatal(errno, "error calling fdopen");
+    disorder_fatal(errno, "error calling fdopen");
   if(fprintf(fp, "password %s\n", pwhex) < 0
      || fclose(fp) < 0)
-    fatal(errno, "error writing to %s", configpathtmp);
+    disorder_fatal(errno, "error writing to %s", configpathtmp);
 
   /* Rename config file into place */
   if(rename(configpathtmp, configpath) < 0)
-    fatal(errno, "error renaming %s to %s", configpathtmp, configpath);
+    disorder_fatal(errno, "error renaming %s to %s", configpathtmp, configpath);
 
   /* Put our identity back */
   if(seteuid(old_uid) < 0)
-    fatal(errno, "seteuid %lu", (unsigned long)old_uid);
+    disorder_fatal(errno, "seteuid %lu", (unsigned long)old_uid);
   if(setegid(old_gid) < 0)
-    fatal(errno, "setegid %lu", (unsigned long)old_gid);
+    disorder_fatal(errno, "setegid %lu", (unsigned long)old_gid);
   
   return 0;
 }
index 8dc6d7f..1d55b8d 100644 (file)
@@ -32,6 +32,8 @@
 #include <unistd.h>
 #include <pcre.h>
 #include <ctype.h>
+#include <gcrypt.h>
+#include <langinfo.h>
 
 #include "configuration.h"
 #include "syscalls.h"
@@ -50,6 +52,8 @@
 #include "vector.h"
 #include "version.h"
 #include "dateparse.h"
+#include "trackdb.h"
+#include "inputline.h"
 
 static disorder_client *client;
 
@@ -63,6 +67,7 @@ static const struct option options[] = {
   { "help-commands", no_argument, 0, 'H' },
   { "user", required_argument, 0, 'u' },
   { "password", required_argument, 0, 'p' },
+  { "wait-for-root", no_argument, 0, 'W' },
   { 0, 0, 0, 0 }
 };
 
@@ -157,7 +162,8 @@ static void cf_shutdown(char attribute((unused)) **argv) {
 
 static void cf_reconfigure(char attribute((unused)) **argv) {
   /* Re-check configuration for server */
-  if(config_read(1)) fatal(0, "cannot read configuration");
+  if(config_read(1, NULL))
+    disorder_fatal(0, "cannot read configuration");
   if(disorder_reconfigure(getclient())) exit(EXIT_FAILURE);
 }
 
@@ -185,15 +191,34 @@ static void cf_queue(char attribute((unused)) **argv) {
 }
 
 static void cf_quack(char attribute((unused)) **argv) {
-  xprintf("\n"
-         " .------------------.\n"
-         " | Naath is a babe! |\n"
-         " `---------+--------'\n"
-         "            \\\n"
-         "              >0\n"
-         "               (<)'\n"
-         "~~~~~~~~~~~~~~~~~~~~~~\n"
-         "\n");
+  if(!strcasecmp(nl_langinfo(CODESET), "utf-8")) {
+#define TL "\xE2\x95\xAD"
+#define TR "\xE2\x95\xAE"
+#define BR "\xE2\x95\xAF"
+#define BL "\xE2\x95\xB0"
+#define H "\xE2\x94\x80"
+#define V "\xE2\x94\x82"
+#define T "\xE2\x94\xAC"
+    xprintf("\n"
+            " "TL H H H H H H H H H H H H H H H H H H TR"\n"
+            " "V" Naath is a babe! "V"\n"
+            " "BL H H H H H H H H H T H H H H H H H H BR"\n"
+            "            \\\n"
+            "              >0\n"
+            "               (<)'\n"
+            "~~~~~~~~~~~~~~~~~~~~~~\n"
+            "\n");
+  } else {
+    xprintf("\n"
+            " .------------------.\n"
+            " | Naath is a babe! |\n"
+            " `---------+--------'\n"
+            "            \\\n"
+            "              >0\n"
+            "               (<)'\n"
+            "~~~~~~~~~~~~~~~~~~~~~~\n"
+            "\n");
+  }
 }
 
 static void cf_somelist(char **argv,
@@ -304,9 +329,9 @@ static void cf_move(char **argv) {
   int e;
   
   if((e = xstrtol(&n, argv[1], 0, 10)))
-    fatal(e, "cannot convert '%s'", argv[1]);
+    disorder_fatal(e, "cannot convert '%s'", argv[1]);
   if(n > INT_MAX || n < INT_MIN)
-    fatal(e, "%ld out of range", n);
+    disorder_fatal(e, "%ld out of range", n);
   if(disorder_move(getclient(), argv[0], (int)n)) exit(EXIT_FAILURE);
 }
 
@@ -463,11 +488,11 @@ static void cf_setup_guest(char **argv) {
     case 'h': help_setup_guest();
     case 'r': online_registration = 1; break;
     case 'R': online_registration = 0; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   if(online_registration && !config->mail_sender)
-    fatal(0, "you MUST set mail_sender if you want online registration");
+    disorder_fatal(0, "you MUST set mail_sender if you want online registration");
   if(disorder_adduser(getclient(), "guest", "",
                      online_registration ? "read,register" : "read"))
     exit(EXIT_FAILURE);
@@ -582,6 +607,61 @@ static void cf_adopt(char **argv) {
     exit(EXIT_FAILURE);
 }
 
+static void cf_playlists(char attribute((unused)) **argv) {
+  char **vec;
+
+  if(disorder_playlists(getclient(), &vec, 0))
+    exit(EXIT_FAILURE);
+  while(*vec)
+    xprintf("%s\n", nullcheck(utf82mb(*vec++)));
+}
+
+static void cf_playlist_del(char **argv) {
+  if(disorder_playlist_delete(getclient(), argv[0]))
+    exit(EXIT_FAILURE);
+}
+
+static void cf_playlist_get(char **argv) {
+  char **vec;
+
+  if(disorder_playlist_get(getclient(), argv[0], &vec, 0))
+    exit(EXIT_FAILURE);
+  while(*vec)
+    xprintf("%s\n", nullcheck(utf82mb(*vec++)));
+}
+
+static void cf_playlist_set(char **argv) {
+  struct vector v[1];
+  FILE *input;
+  const char *tag;
+  char *l;
+
+  if(argv[1]) {
+    // Read track list from file
+    if(!(input = fopen(argv[1], "r")))
+      disorder_fatal(errno, "opening %s", argv[1]);
+    tag = argv[1];
+  } else {
+    // Read track list from standard input
+    input = stdin;
+    tag = "stdin";
+  }
+  vector_init(v);
+  while(!inputline(tag, input, &l, '\n')) {
+    if(!strcmp(l, "."))
+      break;
+    vector_append(v, l);
+  }
+  if(ferror(input))
+    disorder_fatal(errno, "reading %s", tag);
+  if(input != stdin)
+    fclose(input);
+  if(disorder_playlist_lock(getclient(), argv[0])
+     || disorder_playlist_set(getclient(), argv[0], v->vec, v->nvec)
+     || disorder_playlist_unlock(getclient()))
+    exit(EXIT_FAILURE);
+}
+
 static const struct command {
   const char *name;
   int min, max;
@@ -635,6 +715,14 @@ static const struct command {
                       "Add TRACKS to the end of the queue" },
   { "playing",        0, 0, cf_playing, 0, "",
                       "Report the playing track" },
+  { "playlist-del",   1, 1, cf_playlist_del, 0, "PLAYLIST",
+                      "Delete a playlist" },
+  { "playlist-get",   1, 1, cf_playlist_get, 0, "PLAYLIST",
+                      "Get the contents of a playlist" },
+  { "playlist-set",   1, 2, cf_playlist_set, isarg_filename, "PLAYLIST [PATH]",
+                      "Set the contents of a playlist" },
+  { "playlists",      0, 0, cf_playlists, 0, "",
+                      "List playlists" },
   { "prefs",          1, 1, cf_prefs, 0, "TRACK",
                       "Display all the preferences for TRACK" },
   { "quack",          0, 0, cf_quack, 0, 0, 0 },
@@ -727,8 +815,28 @@ static void help_commands(void) {
   exit(0);
 }
 
+static void wait_for_root(void) {
+  const char *password;
+
+  while(!trackdb_readable()) {
+    disorder_info("waiting for trackdb...");
+    sleep(1);
+  }
+  trackdb_init(TRACKDB_NO_RECOVER|TRACKDB_NO_UPGRADE);
+  for(;;) {
+    trackdb_open(TRACKDB_READ_ONLY);
+    password = trackdb_get_password("root");
+    trackdb_close();
+    if(password)
+      break;
+    disorder_info("waiting for root user to be created...");
+    sleep(1);
+  }
+  trackdb_deinit(NULL);
+}
+
 int main(int argc, char **argv) {
-  int n, i, j, local = 0;
+  int n, i, j, local = 0, wfr = 0;
   int status = 0;
   struct vector args;
   const char *user = 0, *password = 0;
@@ -737,9 +845,9 @@ int main(int argc, char **argv) {
   /* garbage-collect PCRE's memory */
   pcre_malloc = xmalloc;
   pcre_free = xfree;
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
-  if(!setlocale(LC_TIME, "")) fatal(errno, "error calling setlocale");
-  while((n = getopt_long(argc, argv, "+hVc:dHlNu:p:", options, 0)) >= 0) {
+  if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_TIME, "")) disorder_fatal(errno, "error calling setlocale");
+  while((n = getopt_long(argc, argv, "+hVc:dHlNu:p:W", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
     case 'H': help_commands();
@@ -750,10 +858,11 @@ int main(int argc, char **argv) {
     case 'N': config_per_user = 0; break;
     case 'u': user = optarg; break;
     case 'p': password = optarg; break;
-    default: fatal(0, "invalid option");
+    case 'W': wfr = 1; break;
+    default: disorder_fatal(0, "invalid option");
     }
   }
-  if(config_read(0)) fatal(0, "cannot read configuration");
+  if(config_read(0, NULL)) disorder_fatal(0, "cannot read configuration");
   if(user) {
     config->username = user;
     config->password = 0;
@@ -761,15 +870,22 @@ int main(int argc, char **argv) {
   if(password)
     config->password = password;
   if(local)
-    config->connect.n = 0;
+    config->connect.af = -1;
+  if(wfr)
+    wait_for_root();
   n = optind;
   optind = 1;                          /* for subsequent getopt calls */
+  /* gcrypt initialization */
+  if(!gcry_check_version(NULL))
+    disorder_fatal(0, "gcry_check_version failed");
+  gcry_control(GCRYCTL_INIT_SECMEM, 0);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
   /* accumulate command args */
   while(n < argc) {
     if((i = TABLE_FIND(commands, name, argv[n])) < 0)
-      fatal(0, "unknown command '%s'", argv[n]);
+      disorder_fatal(0, "unknown command '%s'", argv[n]);
     if(n + commands[i].min >= argc)
-      fatal(0, "missing arguments to '%s'", argv[n]);
+      disorder_fatal(0, "missing arguments to '%s'", argv[n]);
     vector_init(&args);
     /* Include the command name in the args, but at element -1, for
      * the benefit of subcommand getopt calls */
@@ -786,7 +902,7 @@ int main(int argc, char **argv) {
     n += j;
   }
   if(client && disorder_close(client)) exit(EXIT_FAILURE);
-  if(fclose(stdout) < 0) fatal(errno, "error closing stdout");
+  if(fclose(stdout) < 0) disorder_fatal(errno, "error closing stdout");
   return status;
 }
 
index 241b701..ed31dd0 100644 (file)
@@ -139,21 +139,22 @@ static int copy(const char *from, const char *to) {
   struct stat sb;
 
   if((fdin = open(from, O_RDONLY)) < 0)
-    fatal(errno, "error opening %s", from);
+    disorder_fatal(errno, "error opening %s", from);
   if((fdout = open(to, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
-    fatal(errno, "error opening %s", to);
+    disorder_fatal(errno, "error opening %s", to);
   while((n = read(fdin, buffer, sizeof buffer)) > 0) {
     if(write(fdout, buffer, n) < 0)
-      fatal(errno, "error writing to %s", to);
+      disorder_fatal(errno, "error writing to %s", to);
   }
-  if(n < 0) fatal(errno, "error reading %s", from);
+  if(n < 0)
+    disorder_fatal(errno, "error reading %s", from);
   if(fstat(fdin, &sb) < 0)
-    fatal(errno, "error stating %s", from);
+    disorder_fatal(errno, "error stating %s", from);
   if(fchown(fdout, sb.st_uid, sb.st_gid) < 0)
-    fatal(errno, "error chowning %s", from);
+    disorder_fatal(errno, "error chowning %s", from);
   if(fchmod(fdout, sb.st_mode & 07777) < 0)
-    fatal(errno, "error chmoding %s", from);
-  if(close(fdout) < 0) fatal(errno, "error closing %s", to);
+    disorder_fatal(errno, "error chmoding %s", from);
+  if(close(fdout) < 0) disorder_fatal(errno, "error closing %s", to);
   xclose(fdin);
   return 0;
 }
@@ -288,25 +289,25 @@ static void visit(const char *path, const char *destpath) {
    * directory. In that case we'd better not descend into it when we encounter
    * it in the source. */
   if(!strcmp(fullsourcepath, destination)) {
-    info("%s matches destination directory, not recursing", errsourcepath);
+    disorder_info("%s matches destination directory, not recursing", errsourcepath);
     return;
   }
   
   /* Find out what kind of file we're dealing with */
   if(stat(fullsourcepath, &sb) < 0) {
-    error(errno, "cannot stat %s", errsourcepath );
+    disorder_error(errno, "cannot stat %s", errsourcepath );
     ++errors;
     return;
   }
   if(S_ISREG(sb.st_mode)) {
     if(copier != nocopy)
       if(unlink(fulldestpath) < 0 && errno != ENOENT) {
-        error(errno, "cannot remove %s", errdestpath);
+        disorder_error(errno, "cannot remove %s", errdestpath);
         ++errors;
         return;
       }
     if(copier(fullsourcepath, fulldestpath) < 0) {
-      error(errno, "cannot link %s to %s", errsourcepath, errdestpath);
+      disorder_error(errno, "cannot link %s to %s", errsourcepath, errdestpath);
       ++errors;
       return;
     }
@@ -322,22 +323,22 @@ static void visit(const char *path, const char *destpath) {
         /* Created new directory.  Adjust permissions and ownership to match the
          * old one. */
         if(chown(fulldestpath, sb.st_uid, sb.st_gid) < 0) {
-          error(errno, "cannot chown %s", errdestpath);
+          disorder_error(errno, "cannot chown %s", errdestpath);
           ++errors;
         }
         if(chmod(fulldestpath, sb.st_mode & 07777) < 0) {
-          error(errno, "cannot chmod %s", errdestpath);
+          disorder_error(errno, "cannot chmod %s", errdestpath);
           ++errors;
         }
       }
     } else if(errno != EEXIST) {
-      error(errno, "cannot mkdir %s", errdestpath);
+      disorder_error(errno, "cannot mkdir %s", errdestpath);
       ++errors;
       return;
     }
     /* We read the directory and visit all the files in it in any old order. */
     if(!(dp = opendir(fullsourcepath))) {
-      error(errno, "cannot open directory %s", errsourcepath);
+      disorder_error(errno, "cannot open directory %s", errsourcepath);
       ++errors;
       return;
     }
@@ -354,11 +355,12 @@ static void visit(const char *path, const char *destpath) {
       }
       visit(childpath, childdestpath);
     }
-    if(errno) fatal(errno, "error reading directory %s", errsourcepath);
+    if(errno)
+      disorder_fatal(errno, "error reading directory %s", errsourcepath);
     closedir(dp);
   } else {
     /* We don't handle special files, but we'd better warn the user. */
-    info("ignoring %s", errsourcepath);
+    disorder_info("ignoring %s", errsourcepath);
   }
 }
 
@@ -367,7 +369,8 @@ int main(int argc, char **argv) {
   struct pattern *p;
 
   mem_init();
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_CTYPE, ""))
+    disorder_fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "hVdf:t:i:e:ET:u:wlscn", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
@@ -393,17 +396,19 @@ int main(int argc, char **argv) {
     case 's': copier = symlink; break;
     case 'c': copier = copy; break;
     case 'n': copier = nocopy; dirmaker = nomkdir; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
-  if(optind == argc) fatal(0, "missing SOURCE and DESTINATION arguments");
-  else if(optind + 1 == argc) fatal(0, "missing DESTINATION argument");
-  else if(optind + 2 != argc) fatal(0, "redundant extra arguments");
-  if(extracttags) fatal(0, "--extract-tags is not implemented yet"); /* TODO */
+  if(optind == argc)
+    disorder_fatal(0, "missing SOURCE and DESTINATION arguments");
+  else if(optind + 1 == argc) disorder_fatal(0, "missing DESTINATION argument");
+  else if(optind + 2 != argc) disorder_fatal(0, "redundant extra arguments");
+  if(extracttags)
+    disorder_fatal(0, "--extract-tags is not implemented yet"); /* TODO */
   if(tagencoding && !extracttags)
-    fatal(0, "--tag-encoding without --extra-tags does not make sense");
+    disorder_fatal(0, "--tag-encoding without --extra-tags does not make sense");
   if(untagged && !extracttags)
-    fatal(0, "--untagged without --extra-tags does not make sense");
+    disorder_fatal(0, "--untagged without --extra-tags does not make sense");
   source = argv[optind];
   destination = argv[optind + 1];
   nativeencoding = nl_langinfo(CODESET);
diff --git a/clients/playrtp-alsa.c b/clients/playrtp-alsa.c
deleted file mode 100644 (file)
index a407b2f..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * This file is part of DisOrder.
- * Copyright (C) 2008 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file clients/playrtp-alsa.c
- * @brief RTP player - ALSA support
- *
- * This has been rewritten to use the @ref alsabg.h interface and is therefore
- * now closely modelled on @ref playrtp-coreaudio.c.  Given a similar interface
- * wrapping OSS the whole of playrtp could probably be greatly simplified.
- */
-
-#include "common.h"
-
-#if HAVE_ALSA_ASOUNDLIB_H 
-
-#include <poll.h>
-#include <alsa/asoundlib.h>
-#include <pthread.h>
-#include <arpa/inet.h>
-
-#include "mem.h"
-#include "log.h"
-#include "vector.h"
-#include "heap.h"
-#include "playrtp.h"
-#include "alsabg.h"
-
-/** @brief Callback from alsa_bg_collect() */
-static int playrtp_alsa_supply(void *dst,
-                               unsigned supply_nsamples) {
-  unsigned samples_available;
-  const struct packet *p;
-
-  pthread_mutex_lock(&lock);
-  p = playrtp_next_packet();
-  if(p && contains(p, next_timestamp)) {
-    /* This packet is ready to play */
-    const uint32_t packet_end = p->timestamp + p->nsamples;
-    const uint32_t offset = next_timestamp - p->timestamp;
-    const uint16_t *src = (void *)(p->samples_raw + offset);
-    samples_available = packet_end - next_timestamp;
-    if(samples_available > supply_nsamples)
-      samples_available = supply_nsamples;
-    next_timestamp += samples_available;
-    memcpy(dst, src, samples_available * sizeof (int16_t));
-    /* We don't bother junking the packet - that'll be dealt with next time
-     * round */
-  } else {
-    /* No packet is ready to play (and there might be no packet at all) */
-    samples_available = p ? p->timestamp - next_timestamp : supply_nsamples;
-    if(samples_available > supply_nsamples)
-      samples_available = supply_nsamples;
-    /*info("infill %d", samples_available);*/
-    next_timestamp += samples_available;
-    /* Unlike Core Audio the buffer is not guaranteed to be 0-filled */
-    memset(dst, 0, samples_available * sizeof (int16_t));
-  }
-  pthread_mutex_unlock(&lock);
-  return samples_available;
-}
-
-void playrtp_alsa(void) {
-  alsa_bg_init(device ? device : "default",
-               playrtp_alsa_supply);
-  pthread_mutex_lock(&lock);
-  for(;;) {
-    /* Wait for the buffer to fill up a bit */
-    playrtp_fill_buffer();
-    /* Start playing now */
-    info("Playing...");
-    next_timestamp = pheap_first(&packets)->timestamp;
-    active = 1;
-    alsa_bg_enable();
-    /* Wait until the buffer empties out */
-    while(nsamples >= minbuffer
-         || (nsamples > 0
-             && contains(pheap_first(&packets), next_timestamp))) {
-      pthread_cond_wait(&cond, &lock);
-    }
-    /* Stop playing for a bit until the buffer re-fills */
-    alsa_bg_disable();
-    active = 0;
-    /* Go back round */
-  }
-}
-
-#endif
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-indent-tabs-mode:nil
-End:
-*/
diff --git a/clients/playrtp-coreaudio.c b/clients/playrtp-coreaudio.c
deleted file mode 100644 (file)
index 809c89c..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * This file is part of DisOrder.
- * Copyright (C) 2007 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file clients/playrtp-coreaudio.c
- * @brief RTP player - Core Audio support
- */
-
-#include "common.h"
-
-#if HAVE_COREAUDIO_AUDIOHARDWARE_H
-#include <pthread.h>
-#include <CoreAudio/AudioHardware.h>
-
-#include "mem.h"
-#include "log.h"
-#include "vector.h"
-#include "heap.h"
-#include "playrtp.h"
-
-/** @brief Callback from Core Audio */
-static OSStatus adioproc
-    (AudioDeviceID attribute((unused)) inDevice,
-     const AudioTimeStamp attribute((unused)) *inNow,
-     const AudioBufferList attribute((unused)) *inInputData,
-     const AudioTimeStamp attribute((unused)) *inInputTime,
-     AudioBufferList *outOutputData,
-     const AudioTimeStamp attribute((unused)) *inOutputTime,
-     void attribute((unused)) *inClientData) {
-  UInt32 nbuffers = outOutputData->mNumberBuffers;
-  AudioBuffer *ab = outOutputData->mBuffers;
-  uint32_t samples_available;
-
-  pthread_mutex_lock(&lock);
-  while(nbuffers > 0) {
-    float *samplesOut = ab->mData;
-    size_t samplesOutLeft = ab->mDataByteSize / sizeof (float);
-
-    while(samplesOutLeft > 0) {
-      const struct packet *p = playrtp_next_packet();
-      if(p && contains(p, next_timestamp)) {
-        /* This packet is ready to play */
-        const uint32_t packet_end = p->timestamp + p->nsamples;
-        const uint32_t offset = next_timestamp - p->timestamp;
-        const uint16_t *ptr = (void *)(p->samples_raw + offset);
-
-        samples_available = packet_end - next_timestamp;
-        if(samples_available > samplesOutLeft)
-          samples_available = samplesOutLeft;
-        next_timestamp += samples_available;
-        samplesOutLeft -= samples_available;
-        if(dump_buffer) {
-          size_t n;
-
-          for(n = 0; n < samples_available; ++n) {
-            dump_buffer[dump_index++] = (int16_t)ntohs(ptr[n]);
-            dump_index %= dump_size;
-          }
-        }
-        while(samples_available-- > 0)
-          *samplesOut++ = (int16_t)ntohs(*ptr++) * (0.5 / 32767);
-        /* We don't bother junking the packet - that'll be dealt with next time
-         * round */
-      } else {
-        /* No packet is ready to play (and there might be no packet at all) */
-        samples_available = p ? p->timestamp - next_timestamp
-                              : samplesOutLeft;
-        if(samples_available > samplesOutLeft)
-          samples_available = samplesOutLeft;
-        //info("infill by %"PRIu32, samples_available);
-        /* Conveniently the buffer is 0 to start with */
-        next_timestamp += samples_available;
-        samplesOut += samples_available;
-        samplesOutLeft -= samples_available;
-        if(dump_buffer) {
-          size_t n;
-
-          for(n = 0; n < samples_available; ++n) {
-            dump_buffer[dump_index++] = 0;
-            dump_index %= dump_size;
-          }
-        }
-      }
-    }
-    ++ab;
-    --nbuffers;
-  }
-  pthread_mutex_unlock(&lock);
-  return 0;
-}
-
-void playrtp_coreaudio(void) {
-  OSStatus status;
-  UInt32 propertySize;
-  AudioDeviceID adid;
-  AudioStreamBasicDescription asbd;
-
-  /* If this looks suspiciously like libao's macosx driver there's an
-   * excellent reason for that... */
-
-  /* TODO report errors as strings not numbers */
-  propertySize = sizeof adid;
-  status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
-                                   &propertySize, &adid);
-  if(status)
-    fatal(0, "AudioHardwareGetProperty: %d", (int)status);
-  if(adid == kAudioDeviceUnknown)
-    fatal(0, "no output device");
-  propertySize = sizeof asbd;
-  status = AudioDeviceGetProperty(adid, 0, false,
-                                 kAudioDevicePropertyStreamFormat,
-                                 &propertySize, &asbd);
-  if(status)
-    fatal(0, "AudioHardwareGetProperty: %d", (int)status);
-  D(("mSampleRate       %f", asbd.mSampleRate));
-  D(("mFormatID         %08lx", asbd.mFormatID));
-  D(("mFormatFlags      %08lx", asbd.mFormatFlags));
-  D(("mBytesPerPacket   %08lx", asbd.mBytesPerPacket));
-  D(("mFramesPerPacket  %08lx", asbd.mFramesPerPacket));
-  D(("mBytesPerFrame    %08lx", asbd.mBytesPerFrame));
-  D(("mChannelsPerFrame %08lx", asbd.mChannelsPerFrame));
-  D(("mBitsPerChannel   %08lx", asbd.mBitsPerChannel));
-  D(("mReserved         %08lx", asbd.mReserved));
-  if(asbd.mFormatID != kAudioFormatLinearPCM)
-    fatal(0, "audio device does not support kAudioFormatLinearPCM");
-  status = AudioDeviceAddIOProc(adid, adioproc, 0);
-  if(status)
-    fatal(0, "AudioDeviceAddIOProc: %d", (int)status);
-  pthread_mutex_lock(&lock);
-  for(;;) {
-    /* Wait for the buffer to fill up a bit */
-    playrtp_fill_buffer();
-    /* Start playing now */
-    info("Playing...");
-    next_timestamp = pheap_first(&packets)->timestamp;
-    active = 1;
-    status = AudioDeviceStart(adid, adioproc);
-    if(status)
-      fatal(0, "AudioDeviceStart: %d", (int)status);
-    /* Wait until the buffer empties out */
-    while(nsamples >= minbuffer
-         || (nsamples > 0
-             && contains(pheap_first(&packets), next_timestamp)))
-      pthread_cond_wait(&cond, &lock);
-    /* Stop playing for a bit until the buffer re-fills */
-    status = AudioDeviceStop(adid, adioproc);
-    if(status)
-      fatal(0, "AudioDeviceStop: %d", (int)status);
-    active = 0;
-    /* Go back round */
-  }
-}
-
-#endif
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-indent-tabs-mode:nil
-End:
-*/
diff --git a/clients/playrtp-oss.c b/clients/playrtp-oss.c
deleted file mode 100644 (file)
index 4b0ab83..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * This file is part of DisOrder.
- * Copyright (C) 2007 Richard Kettlewell
- * Portions copyright (C) 2007 Ross Younger
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file clients/playrtp-oss.c
- * @brief RTP player - OSS and empeg support
- */
-
-#include "common.h"
-
-#if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
-
-#include <poll.h>
-#include <sys/ioctl.h>
-#if !EMPEG_HOST
-#include <sys/soundcard.h>
-#endif
-#include <pthread.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <arpa/inet.h>
-
-#include "mem.h"
-#include "log.h"
-#include "vector.h"
-#include "heap.h"
-#include "syscalls.h"
-#include "playrtp.h"
-
-/** @brief /dev/dsp (or whatever) */
-static int playrtp_oss_fd = -1;
-
-/** @brief Audio buffer */
-static char *playrtp_oss_buffer;
-
-/** @brief Size of @ref playrtp_oss_buffer in bytes */
-static int playrtp_oss_bufsize;
-
-/** @brief Number of bytes used in @ref playrtp_oss_buffer */
-static int playrtp_oss_bufused;
-
-/** @brief Open and configure the OSS audio device */
-static void playrtp_oss_enable(void) {
-  if(playrtp_oss_fd == -1) {
-#if EMPEG_HOST
-    /* empeg audio driver only knows /dev/audio, only supports the equivalent
-     * of AFMT_S16_NE, has a fixed buffer size, and does not support the
-     * SNDCTL_ ioctls. */
-    if(!device)
-      device = "/dev/audio";
-    if((playrtp_oss_fd = open(device, O_WRONLY)) < 0)
-      fatal(errno, "error opening %s", device);
-    playrtp_oss_bufsize = 4608;
-#else
-    int rate = 44100, stereo = 1, format = AFMT_S16_BE;
-    if(!device) {
-      if(access("/dev/dsp", W_OK) == 0)
-       device = "/dev/dsp";
-      else if(access("/dev/audio", W_OK) == 0)
-       device = "/dev/audio";
-      else
-       fatal(0, "cannot determine default audio device");
-    }
-    if((playrtp_oss_fd = open(device, O_WRONLY)) < 0)
-      fatal(errno, "error opening %s", device);
-    if(ioctl(playrtp_oss_fd, SNDCTL_DSP_SETFMT, &format) < 0)
-      fatal(errno, "ioctl SNDCTL_DSP_SETFMT");
-    if(ioctl(playrtp_oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
-      fatal(errno, "ioctl SNDCTL_DSP_STEREO");
-    if(ioctl(playrtp_oss_fd, SNDCTL_DSP_SPEED, &rate) < 0)
-      fatal(errno, "ioctl SNDCTL_DSP_SPEED");
-    if(rate != 44100)
-      error(0, "asking for 44100Hz, got %dHz", rate);
-    if(ioctl(playrtp_oss_fd, SNDCTL_DSP_GETBLKSIZE, &playrtp_oss_bufsize) < 0)
-      fatal(errno, "ioctl SNDCTL_DSP_GETBLKSIZE");
-    info("OSS buffer size %d", playrtp_oss_bufsize);
-#endif
-    playrtp_oss_buffer = xmalloc(playrtp_oss_bufsize);
-    playrtp_oss_bufused = 0;
-    nonblock(playrtp_oss_fd);
-  }
-}
-
-/** @brief Flush the OSS output buffer
- * @return 0 on success, non-0 on error
- */
-static int playrtp_oss_flush(void) {
-  int nbyteswritten;
-
-  if(!playrtp_oss_bufused)
-    return 0;                           /* nothing to do */
-  /* 0 out the unused portion of the buffer */
-  memset(playrtp_oss_buffer + playrtp_oss_bufused, 0,
-         playrtp_oss_bufsize - playrtp_oss_bufused);
-#if EMPEG_HOST 
-  /* empeg audio driver insists on native-endian samples */
-  {
-    uint16_t *ptr,
-      *const limit = (uint16_t *)(playrtp_oss_buffer + playrtp_oss_bufused);
-
-    for(ptr = (uint16_t *)playrtp_oss_buffer; ptr < limit; ++ptr)
-      *ptr = ntohs(*ptr);
-  }
-#endif
-  for(;;) {
-    nbyteswritten = write(playrtp_oss_fd,
-                          playrtp_oss_buffer, playrtp_oss_bufsize);
-    if(nbyteswritten < 0) {
-      switch(errno) {
-      case EINTR:
-        break;                          /* try again */
-      case EAGAIN:
-        return 0;                       /* try later */
-      default:
-        error(errno, "error writing to %s", device);
-        return -1;
-      }
-    } else {
-      if(nbyteswritten < playrtp_oss_bufsize)
-        error(0, "%s: short write (%d/%d)",
-              device, nbyteswritten, playrtp_oss_bufsize);
-      if(dump_buffer) {
-        int count;
-        const int16_t *sp = (const int16_t *)playrtp_oss_buffer;
-        
-        for(count = 0; count < playrtp_oss_bufsize; count += sizeof(int16_t)) {
-          dump_buffer[dump_index++] = (int16_t)ntohs(*sp++);
-          dump_index %= dump_size;
-        }
-      }
-      playrtp_oss_bufused = 0;
-      return 0;
-    }
-  }
-}
-
-/** @brief Wait until the audio device can accept more data */
-static void playrtp_oss_wait(void) {
-  struct pollfd fds[1];
-  int n;
-
-  do {
-    fds[0].fd = playrtp_oss_fd;
-    fds[0].events = POLLOUT;
-    while((n = poll(fds, 1, -1)) < 0 && errno == EINTR)
-      ;
-    if(n < 0)
-      fatal(errno, "calling poll");
-  } while(!(fds[0].revents & (POLLOUT|POLLERR)));
-}
-
-/** @brief Close the OSS output device
- * @param hard If nonzero, drop pending data
- */
-static void playrtp_oss_disable(int hard) {
-  if(hard) {
-#if !EMPEG_HOST
-    /* No SNDCTL_DSP_ ioctls on empeg */
-    if(ioctl(playrtp_oss_fd, SNDCTL_DSP_RESET, 0) < 0)
-      error(errno, "ioctl SNDCTL_DSP_RESET");
-#endif
-  } else
-    playrtp_oss_flush();
-  xclose(playrtp_oss_fd);
-  playrtp_oss_fd = -1;
-  free(playrtp_oss_buffer);
-  playrtp_oss_buffer = 0;
-}
-
-/** @brief Write samples to OSS output device
- * @param data Pointer to sample data
- * @param samples Number of samples
- * @return 0 on success, non-0 on error
- */
-static int playrtp_oss_write(const char *data, size_t samples) {
-  long bytes = samples * sizeof(int16_t);
-  while(bytes > 0) {
-    int n = playrtp_oss_bufsize - playrtp_oss_bufused;
-
-    if(n > bytes)
-      n = bytes;
-    memcpy(playrtp_oss_buffer + playrtp_oss_bufused, data, n);
-    bytes -= n;
-    data += n;
-    playrtp_oss_bufused += n;
-    if(playrtp_oss_bufused == playrtp_oss_bufsize)
-      if(playrtp_oss_flush())
-        return -1;
-  }
-  next_timestamp += samples;
-  return 0;
-}
-
-/** @brief Play some data from packet @p p
- *
- * @p p is assumed to contain @ref next_timestamp.
- */
-static int playrtp_oss_play(const struct packet *p) {
-  return playrtp_oss_write
-    ((const char *)(p->samples_raw + next_timestamp - p->timestamp),
-     (p->timestamp + p->nsamples) - next_timestamp);
-}
-
-/** @brief Play some silence before packet @p p
- *
- * @p p is assumed to be entirely before @ref next_timestamp.
- */
-static int playrtp_oss_infill(const struct packet *p) {
-  static const char zeros[INFILL_SAMPLES * sizeof(int16_t)];
-  size_t samples_available = INFILL_SAMPLES;
-
-  if(p && samples_available > p->timestamp - next_timestamp)
-    samples_available = p->timestamp - next_timestamp;
-  return playrtp_oss_write(zeros, samples_available);
-}
-
-/** @brief OSS backend for playrtp */
-void playrtp_oss(void) {
-  int escape;
-  const struct packet *p;
-
-  pthread_mutex_lock(&lock);
-  for(;;) {
-    /* Wait for the buffer to fill up a bit */
-    playrtp_fill_buffer();
-    playrtp_oss_enable();
-    escape = 0;
-    info("Playing...");
-    /* Keep playing until the buffer empties out, we get an error */
-    while((nsamples >= minbuffer
-          || (nsamples > 0
-              && contains(pheap_first(&packets), next_timestamp)))
-         && !escape) {
-      /* Wait until we can play more */
-      pthread_mutex_unlock(&lock);
-      playrtp_oss_wait();
-      pthread_mutex_lock(&lock);
-      /* Device is ready for more data, find something to play */
-      p = playrtp_next_packet();
-      /* Play it or play some silence */
-      if(contains(p, next_timestamp))
-       escape = playrtp_oss_play(p);
-      else
-       escape = playrtp_oss_infill(p);
-    }
-    active = 0;
-    /* We stop playing for a bit until the buffer re-fills */
-    pthread_mutex_unlock(&lock);
-    playrtp_oss_disable(escape);
-    pthread_mutex_lock(&lock);
-  }
-}
-
-#endif
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-indent-tabs-mode:nil
-End:
-*/
index aca1532..2ab6790 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2007-2009 Richard Kettlewell
  *
  * 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
  * systems.  There is no support for Microsoft Windows yet, and that will in
  * fact probably an entirely separate program.
  *
- * The program runs (at least) three threads.  listen_thread() is responsible
- * for reading RTP packets off the wire and adding them to the linked list @ref
- * received_packets, assuming they are basically sound.  queue_thread() takes
- * packets off this linked list and adds them to @ref packets (an operation
- * which might be much slower due to contention for @ref lock).
+ * The program runs (at least) three threads:
  *
- * The main thread is responsible for actually playing audio.  In ALSA this
- * means it waits until ALSA says it's ready for more audio which it then
- * plays.  See @ref clients/playrtp-alsa.c.
+ * listen_thread() is responsible for reading RTP packets off the wire and
+ * adding them to the linked list @ref received_packets, assuming they are
+ * basically sound.
  *
- * In Core Audio the main thread is only responsible for starting and stopping
- * play: the system does the actual playback in its own private thread, and
- * calls adioproc() to fetch the audio data.  See @ref
- * clients/playrtp-coreaudio.c.
+ * queue_thread() takes packets off this linked list and adds them to @ref
+ * packets (an operation which might be much slower due to contention for @ref
+ * lock).
+ *
+ * control_thread() accepts commands from Disobedience (or anything else).
+ *
+ * The main thread activates and deactivates audio playing via the @ref
+ * lib/uaudio.h API (which probably implies at least one further thread).
  *
  * Sometimes it happens that there is no audio available to play.  This may
  * because the server went away, or a packet was dropped, or the server
@@ -64,6 +64,7 @@
 #include <unistd.h>
 #include <sys/mman.h>
 #include <fcntl.h>
+#include <math.h>
 
 #include "log.h"
 #include "mem.h"
@@ -79,8 +80,7 @@
 #include "playrtp.h"
 #include "inputline.h"
 #include "version.h"
-
-#define readahead linux_headers_are_borked
+#include "uaudio.h"
 
 /** @brief Obsolete synonym */
 #ifndef IPV6_JOIN_GROUP
@@ -94,21 +94,14 @@ static int rtpfd;
 static FILE *logfp;
 
 /** @brief Output device */
-const char *device;
-
-/** @brief Minimum low watermark
- *
- * We'll stop playing if there's only this many samples in the buffer. */
-unsigned minbuffer = 2 * 44100 / 10;  /* 0.2 seconds */
 
-/** @brief Buffer high watermark
- *
- * We'll only start playing when this many samples are available. */
-static unsigned readahead = 2 * 2 * 44100;
+/** @brief Buffer low watermark in samples */
+unsigned minbuffer = 4 * (2 * 44100) / 10;  /* 0.4 seconds */
 
-/** @brief Maximum buffer size
+/** @brief Maximum buffer size in samples
  *
- * We'll stop reading from the network if we have this many samples. */
+ * We'll stop reading from the network if we have this many samples.
+ */
 static unsigned maxbuffer;
 
 /** @brief Received packets
@@ -168,16 +161,8 @@ pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
 /** @brief Condition variable signalled whenever @ref packets is changed */
 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
 
-#if DEFAULT_BACKEND == BACKEND_ALSA
-# define DEFAULT_PLAYRTP_BACKEND playrtp_alsa
-#elif DEFAULT_BACKEND == BACKEND_OSS
-# define DEFAULT_PLAYRTP_BACKEND playrtp_oss
-#elif DEFAULT_BACKEND == BACKEND_COREAUDIO
-# define DEFAULT_PLAYRTP_BACKEND playrtp_coreaudio
-#endif
-
 /** @brief Backend to play with */
-static void (*backend)(void) = DEFAULT_PLAYRTP_BACKEND;
+static const struct uaudio *backend;
 
 HEAP_DEFINE(pheap, struct packet *, lt_packet);
 
@@ -212,7 +197,6 @@ static const struct option options[] = {
   { "device", required_argument, 0, 'D' },
   { "min", required_argument, 0, 'm' },
   { "max", required_argument, 0, 'x' },
-  { "buffer", required_argument, 0, 'b' },
   { "rcvbuf", required_argument, 0, 'R' },
 #if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
   { "oss", no_argument, 0, 'o' },
@@ -224,8 +208,11 @@ static const struct option options[] = {
   { "core-audio", no_argument, 0, 'c' },
 #endif
   { "dump", required_argument, 0, 'r' },
+  { "command", required_argument, 0, 'e' },
+  { "pause-mode", required_argument, 0, 'P' },
   { "socket", required_argument, 0, 's' },
   { "config", required_argument, 0, 'C' },
+  { "monitor", no_argument, 0, 'M' },
   { 0, 0, 0, 0 }
 };
 
@@ -260,10 +247,10 @@ static void *control_thread(void attribute((unused)) *arg) {
   strcpy(sa.sun_path, control_socket);
   sfd = xsocket(PF_UNIX, SOCK_STREAM, 0);
   if(bind(sfd, (const struct sockaddr *)&sa, sizeof sa) < 0)
-    fatal(errno, "error binding to %s", control_socket);
+    disorder_fatal(errno, "error binding to %s", control_socket);
   if(listen(sfd, 128) < 0)
-    fatal(errno, "error calling listen on %s", control_socket);
-  info("listening on %s", control_socket);
+    disorder_fatal(errno, "error calling listen on %s", control_socket);
+  disorder_info("listening on %s", control_socket);
   for(;;) {
     salen = sizeof sa;
     cfd = accept(sfd, (struct sockaddr *)&sa, &salen);
@@ -273,17 +260,17 @@ static void *control_thread(void attribute((unused)) *arg) {
       case EAGAIN:
         break;
       default:
-        fatal(errno, "error calling accept on %s", control_socket);
+        disorder_fatal(errno, "error calling accept on %s", control_socket);
       }
     }
     if(!(fp = fdopen(cfd, "r+"))) {
-      error(errno, "error calling fdopen for %s connection", control_socket);
+      disorder_error(errno, "error calling fdopen for %s connection", control_socket);
       close(cfd);
       continue;
     }
     if(!inputline(control_socket, fp, &line, '\n')) {
       if(!strcmp(line, "stop")) {
-        info("stopped via %s", control_socket);
+        disorder_info("stopped via %s", control_socket);
         exit(0);                          /* terminate immediately */
       }
       if(!strcmp(line, "query"))
@@ -291,7 +278,7 @@ static void *control_thread(void attribute((unused)) *arg) {
       xfree(line);
     }
     if(fclose(fp) < 0)
-      error(errno, "error closing %s connection", control_socket);
+      disorder_error(errno, "error closing %s connection", control_socket);
   }
 }
 
@@ -337,6 +324,9 @@ static void *queue_thread(void attribute((unused)) *arg) {
     pthread_cond_broadcast(&cond);
     pthread_mutex_unlock(&lock);
   }
+#if HAVE_STUPID_GCC44
+  return NULL;
+#endif
 }
 
 /** @brief Background thread collecting samples
@@ -381,19 +371,19 @@ static void *listen_thread(void attribute((unused)) *arg) {
       case EINTR:
         continue;
       default:
-        fatal(errno, "error reading from socket");
+        disorder_fatal(errno, "error reading from socket");
       }
     }
     /* Ignore too-short packets */
     if((size_t)n <= sizeof (struct rtp_header)) {
-      info("ignored a short packet");
+      disorder_info("ignored a short packet");
       continue;
     }
     timestamp = htonl(header.timestamp);
     seq = htons(header.seq);
     /* Ignore packets in the past */
     if(active && lt(timestamp, next_timestamp)) {
-      info("dropping old packet, timestamp=%"PRIx32" < %"PRIx32,
+      disorder_info("dropping old packet, timestamp=%"PRIx32" < %"PRIx32,
            timestamp, next_timestamp);
       continue;
     }
@@ -407,14 +397,21 @@ static void *listen_thread(void attribute((unused)) *arg) {
     if(header.mpt & 0x80)
       p->flags |= IDLE;
     switch(header.mpt & 0x7F) {
-    case 10:
+    case 10:                            /* L16 */
       p->nsamples = (n - sizeof header) / sizeof(uint16_t);
       break;
       /* TODO support other RFC3551 media types (when the speaker does) */
     default:
-      fatal(0, "unsupported RTP payload type %d",
-            header.mpt & 0x7F);
+      disorder_fatal(0, "unsupported RTP payload type %d", header.mpt & 0x7F);
     }
+    /* See if packet is silent */
+    const uint16_t *s = p->samples_raw;
+    n = p->nsamples;
+    for(; n > 0; --n)
+      if(*s++)
+        break;
+    if(!n)
+      p->flags |= SILENT;
     if(logfp)
       fprintf(logfp, "sequence %u timestamp %"PRIx32" length %"PRIx32" end %"PRIx32"\n",
               seq, timestamp, p->nsamples, timestamp + p->nsamples);
@@ -446,12 +443,18 @@ static void *listen_thread(void attribute((unused)) *arg) {
  * Must be called with @ref lock held.
  */
 void playrtp_fill_buffer(void) {
-  while(nsamples)
+  /* Discard current buffer contents */
+  while(nsamples) {
+    //fprintf(stderr, "%8u/%u (%u) DROPPING\n", nsamples, maxbuffer, minbuffer);
     drop_first_packet();
-  info("Buffering...");
-  while(nsamples < readahead) {
+  }
+  disorder_info("Buffering...");
+  /* Wait until there's at least minbuffer samples available */
+  while(nsamples < minbuffer) {
+    //fprintf(stderr, "%8u/%u (%u) FILLING\n", nsamples, maxbuffer, minbuffer);
     pthread_cond_wait(&cond, &lock);
   }
+  /* Start from whatever is earliest */
   next_timestamp = pheap_first(&packets)->timestamp;
   active = 1;
 }
@@ -479,37 +482,13 @@ struct packet *playrtp_next_packet(void) {
   return 0;
 }
 
-/** @brief Play an RTP stream
- *
- * This is the guts of the program.  It is responsible for:
- * - starting the listening thread
- * - opening the audio device
- * - reading ahead to build up a buffer
- * - arranging for audio to be played
- * - detecting when the buffer has got too small and re-buffering
- */
-static void play_rtp(void) {
-  pthread_t ltid;
-  int err;
-
-  /* We receive and convert audio data in a background thread */
-  if((err = pthread_create(&ltid, 0, listen_thread, 0)))
-    fatal(err, "pthread_create listen_thread");
-  /* We have a second thread to add received packets to the queue */
-  if((err = pthread_create(&ltid, 0, queue_thread, 0)))
-    fatal(err, "pthread_create queue_thread");
-  /* The rest of the work is backend-specific */
-  backend();
-}
-
 /* display usage message and terminate */
 static void help(void) {
   xprintf("Usage:\n"
-         "  disorder-playrtp [OPTIONS] ADDRESS [PORT]\n"
+         "  disorder-playrtp [OPTIONS] [[ADDRESS] PORT]\n"
          "Options:\n"
           "  --device, -D DEVICE     Output device\n"
           "  --min, -m FRAMES        Buffer low water mark\n"
-          "  --buffer, -b FRAMES     Buffer high water mark\n"
           "  --max, -x FRAMES        Buffer maximum size\n"
           "  --rcvbuf, -R BYTES      Socket receive buffer size\n"
           "  --config, -C PATH       Set configuration file\n"
@@ -522,6 +501,9 @@ static void help(void) {
 #if HAVE_COREAUDIO_AUDIOHARDWARE_H
           "  --core-audio, -c        Use Core Audio to play audio\n"
 #endif
+          "  --command, -e COMMAND   Pipe audio to command.\n"
+          "  --pause-mode, -P silence  For -e: pauses send silence (default)\n"
+          "  --pause-mode, -P suspend  For -e: pauses suspend writes\n"
          "  --help, -h              Display usage message\n"
          "  --version, -V           Display version number\n"
           );
@@ -529,12 +511,114 @@ static void help(void) {
   exit(0);
 }
 
+static size_t playrtp_callback(void *buffer,
+                               size_t max_samples,
+                               void attribute((unused)) *userdata) {
+  size_t samples;
+  int silent = 0;
+
+  pthread_mutex_lock(&lock);
+  /* Get the next packet, junking any that are now in the past */
+  const struct packet *p = playrtp_next_packet();
+  if(p && contains(p, next_timestamp)) {
+    /* This packet is ready to play; the desired next timestamp points
+     * somewhere into it. */
+
+    /* Timestamp of end of packet */
+    const uint32_t packet_end = p->timestamp + p->nsamples;
+
+    /* Offset of desired next timestamp into current packet */
+    const uint32_t offset = next_timestamp - p->timestamp;
+
+    /* Pointer to audio data */
+    const uint16_t *ptr = (void *)(p->samples_raw + offset);
+
+    /* Compute number of samples left in packet, limited to output buffer
+     * size */
+    samples = packet_end - next_timestamp;
+    if(samples > max_samples)
+      samples = max_samples;
+
+    /* Copy into buffer, converting to native endianness */
+    size_t i = samples;
+    int16_t *bufptr = buffer;
+    while(i > 0) {
+      *bufptr++ = (int16_t)ntohs(*ptr++);
+      --i;
+    }
+    silent = !!(p->flags & SILENT);
+  } else {
+    /* There is no suitable packet.  We introduce 0s up to the next packet, or
+     * to fill the buffer if there's no next packet or that's too many.  The
+     * comparison with max_samples deals with the otherwise troubling overflow
+     * case. */
+    samples = p ? p->timestamp - next_timestamp : max_samples;
+    if(samples > max_samples)
+      samples = max_samples;
+    //info("infill by %zu", samples);
+    memset(buffer, 0, samples * uaudio_sample_size);
+    silent = 1;
+  }
+  /* Debug dump */
+  if(dump_buffer) {
+    for(size_t i = 0; i < samples; ++i) {
+      dump_buffer[dump_index++] = ((int16_t *)buffer)[i];
+      dump_index %= dump_size;
+    }
+  }
+  /* Advance timestamp */
+  next_timestamp += samples;
+  /* If we're getting behind then try to drop just silent packets
+   *
+   * In theory this shouldn't be necessary.  The server is supposed to send
+   * packets at the right rate and compares the number of samples sent with the
+   * time in order to ensure this.
+   *
+   * However, various things could throw this off:
+   *
+   * - the server's clock could advance at the wrong rate.  This would cause it
+   *   to mis-estimate the right number of samples to have sent and
+   *   inappropriately throttle or speed up.
+   *
+   * - playback could happen at the wrong rate.  If the playback host's sound
+   *   card has a slightly incorrect clock then eventually it will get out
+   *   of step.
+   *
+   * So if we play back slightly slower than the server sends for either of
+   * these reasons then eventually our buffer, and the socket's buffer, will
+   * fill, and the kernel will start dropping packets.  The result is audible
+   * and not very nice.
+   *
+   * Therefore if we're getting behind, we pre-emptively drop silent packets,
+   * since a change in the duration of a silence is less noticeable than a
+   * dropped packet from the middle of continuous music.
+   *
+   * (If things go wrong the other way then eventually we run out of packets to
+   * play and are forced to play silence.  This doesn't seem to happen in
+   * practice but if it does then in the same way we can artificially extend
+   * silent packets to compensate.)
+   *
+   * Dropped packets are always logged; use 'disorder-playrtp --monitor' to
+   * track how close to target buffer occupancy we are on a once-a-minute
+   * basis.
+   */
+  if(nsamples > minbuffer && silent) {
+    disorder_info("dropping %zu samples (%"PRIu32" > %"PRIu32")",
+                  samples, nsamples, minbuffer);
+    samples = 0;
+  }
+  /* Junk obsolete packets */
+  playrtp_next_packet();
+  pthread_mutex_unlock(&lock);
+  return samples;
+}
+
 int main(int argc, char **argv) {
   int n, err;
   struct addrinfo *res;
   struct stringlist sl;
   char *sockname;
-  int rcvbuf, target_rcvbuf = 131072;
+  int rcvbuf, target_rcvbuf = 0;
   socklen_t len;
   struct ip_mreq mreq;
   struct ipv6_mreq mreq6;
@@ -548,6 +632,9 @@ int main(int argc, char **argv) {
   };
   union any_sockaddr mgroup;
   const char *dumpfile = 0;
+  pthread_t ltid;
+  int monitor = 0;
+  static const int one = 1;
 
   static const struct addrinfo prefs = {
     .ai_flags = AI_PASSIVE,
@@ -556,37 +643,43 @@ int main(int argc, char **argv) {
     .ai_protocol = IPPROTO_UDP
   };
 
+  /* Timing information is often important to debugging playrtp, so we include
+   * timestamps in the logs */
+  logdate = 1;
   mem_init();
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
-  while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:M:aocC:r", options, 0)) >= 0) {
+  if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
+  backend = uaudio_apis[0];
+  while((n = getopt_long(argc, argv, "hVdD:m:x:L:R:aocC:re:P:M", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
     case 'V': version("disorder-playrtp");
     case 'd': debugging = 1; break;
-    case 'D': device = optarg; break;
+    case 'D': uaudio_set("device", optarg); break;
     case 'm': minbuffer = 2 * atol(optarg); break;
-    case 'b': readahead = 2 * atol(optarg); break;
     case 'x': maxbuffer = 2 * atol(optarg); break;
     case 'L': logfp = fopen(optarg, "w"); break;
     case 'R': target_rcvbuf = atoi(optarg); break;
 #if HAVE_ALSA_ASOUNDLIB_H
-    case 'a': backend = playrtp_alsa; break;
+    case 'a': backend = &uaudio_alsa; break;
 #endif
 #if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
-    case 'o': backend = playrtp_oss; break;
+    case 'o': backend = &uaudio_oss; break;
 #endif
 #if HAVE_COREAUDIO_AUDIOHARDWARE_H      
-    case 'c': backend = playrtp_coreaudio; break;
+    case 'c': backend = &uaudio_coreaudio; break;
 #endif
     case 'C': configfile = optarg; break;
     case 's': control_socket = optarg; break;
     case 'r': dumpfile = optarg; break;
-    default: fatal(0, "invalid option");
+    case 'e': backend = &uaudio_command; uaudio_set("command", optarg); break;
+    case 'P': uaudio_set("pause-mode", optarg); break;
+    case 'M': monitor = 1; break;
+    default: disorder_fatal(0, "invalid option");
     }
   }
-  if(config_read(0)) fatal(0, "cannot read configuration");
+  if(config_read(0, NULL)) disorder_fatal(0, "cannot read configuration");
   if(!maxbuffer)
-    maxbuffer = 4 * readahead;
+    maxbuffer = 2 * minbuffer;
   argc -= optind;
   argv += optind;
   switch(argc) {
@@ -607,7 +700,7 @@ int main(int argc, char **argv) {
     sl.s = argv;
     break;
   default:
-    fatal(0, "usage: disorder-playrtp [OPTIONS] [[ADDRESS] PORT]");
+    disorder_fatal(0, "usage: disorder-playrtp [OPTIONS] [[ADDRESS] PORT]");
   }
   /* Look up address and port */
   if(!(res = get_address(&sl, &prefs, &sockname)))
@@ -616,9 +709,15 @@ int main(int argc, char **argv) {
   if((rtpfd = socket(res->ai_family,
                      res->ai_socktype,
                      res->ai_protocol)) < 0)
-    fatal(errno, "error creating socket");
-  /* Stash the multicast group address */
-  if((is_multicast = multicast(res->ai_addr))) {
+    disorder_fatal(errno, "error creating socket");
+  /* Allow multiple listeners */
+  xsetsockopt(rtpfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
+  is_multicast = multicast(res->ai_addr);
+  /* The multicast and unicast/broadcast cases are different enough that they
+   * are totally split.  Trying to find commonality between them causes more
+   * trouble that it's worth. */
+  if(is_multicast) {
+    /* Stash the multicast group address */
     memcpy(&mgroup, res->ai_addr, res->ai_addrlen);
     switch(res->ai_addr->sa_family) {
     case AF_INET:
@@ -627,67 +726,82 @@ int main(int argc, char **argv) {
     case AF_INET6:
       mgroup.in6.sin6_port = 0;
       break;
+    default:
+      disorder_fatal(0, "unsupported address family %d",
+                     (int)res->ai_addr->sa_family);
     }
-  }
-  /* Bind to 0/port */
-  switch(res->ai_addr->sa_family) {
-  case AF_INET:
-    memset(&((struct sockaddr_in *)res->ai_addr)->sin_addr, 0,
-           sizeof (struct in_addr));
-    break;
-  case AF_INET6:
-    memset(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 0,
-           sizeof (struct in6_addr));
-    break;
-  default:
-    fatal(0, "unsupported family %d", (int)res->ai_addr->sa_family);
-  }
-  if(bind(rtpfd, res->ai_addr, res->ai_addrlen) < 0)
-    fatal(errno, "error binding socket to %s", sockname);
-  if(is_multicast) {
+    /* Bind to to the multicast group address */
+    if(bind(rtpfd, res->ai_addr, res->ai_addrlen) < 0)
+      disorder_fatal(errno, "error binding socket to %s",
+                     format_sockaddr(res->ai_addr));
+    /* Add multicast group membership */
     switch(mgroup.sa.sa_family) {
     case PF_INET:
       mreq.imr_multiaddr = mgroup.in.sin_addr;
       mreq.imr_interface.s_addr = 0;      /* use primary interface */
       if(setsockopt(rtpfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                     &mreq, sizeof mreq) < 0)
-        fatal(errno, "error calling setsockopt IP_ADD_MEMBERSHIP");
+        disorder_fatal(errno, "error calling setsockopt IP_ADD_MEMBERSHIP");
       break;
     case PF_INET6:
       mreq6.ipv6mr_multiaddr = mgroup.in6.sin6_addr;
       memset(&mreq6.ipv6mr_interface, 0, sizeof mreq6.ipv6mr_interface);
       if(setsockopt(rtpfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
                     &mreq6, sizeof mreq6) < 0)
-        fatal(errno, "error calling setsockopt IPV6_JOIN_GROUP");
+        disorder_fatal(errno, "error calling setsockopt IPV6_JOIN_GROUP");
       break;
     default:
-      fatal(0, "unsupported address family %d", res->ai_family);
+      disorder_fatal(0, "unsupported address family %d", res->ai_family);
     }
-    info("listening on %s multicast group %s",
-         format_sockaddr(res->ai_addr), format_sockaddr(&mgroup.sa));
-  } else
-    info("listening on %s", format_sockaddr(res->ai_addr));
+    /* Report what we did */
+    disorder_info("listening on %s multicast group %s",
+                  format_sockaddr(res->ai_addr), format_sockaddr(&mgroup.sa));
+  } else {
+    /* Bind to 0/port */
+    switch(res->ai_addr->sa_family) {
+    case AF_INET: {
+      struct sockaddr_in *in = (struct sockaddr_in *)res->ai_addr;
+      
+      memset(&in->sin_addr, 0, sizeof (struct in_addr));
+      break;
+    }
+    case AF_INET6: {
+      struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)res->ai_addr;
+      
+      memset(&in6->sin6_addr, 0, sizeof (struct in6_addr));
+      break;
+    }
+    default:
+      disorder_fatal(0, "unsupported family %d", (int)res->ai_addr->sa_family);
+    }
+    if(bind(rtpfd, res->ai_addr, res->ai_addrlen) < 0)
+      disorder_fatal(errno, "error binding socket to %s",
+                     format_sockaddr(res->ai_addr));
+    /* Report what we did */
+    disorder_info("listening on %s", format_sockaddr(res->ai_addr));
+  }
   len = sizeof rcvbuf;
   if(getsockopt(rtpfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len) < 0)
-    fatal(errno, "error calling getsockopt SO_RCVBUF");
+    disorder_fatal(errno, "error calling getsockopt SO_RCVBUF");
   if(target_rcvbuf > rcvbuf) {
     if(setsockopt(rtpfd, SOL_SOCKET, SO_RCVBUF,
                   &target_rcvbuf, sizeof target_rcvbuf) < 0)
-      error(errno, "error calling setsockopt SO_RCVBUF %d", 
-            target_rcvbuf);
+      disorder_error(errno, "error calling setsockopt SO_RCVBUF %d", 
+                     target_rcvbuf);
       /* We try to carry on anyway */
     else
-      info("changed socket receive buffer from %d to %d",
-           rcvbuf, target_rcvbuf);
+      disorder_info("changed socket receive buffer from %d to %d",
+                    rcvbuf, target_rcvbuf);
   } else
-    info("default socket receive buffer %d", rcvbuf);
+    disorder_info("default socket receive buffer %d", rcvbuf);
+  //info("minbuffer %u maxbuffer %u", minbuffer, maxbuffer);
   if(logfp)
-    info("WARNING: -L option can impact performance");
+    disorder_info("WARNING: -L option can impact performance");
   if(control_socket) {
     pthread_t tid;
 
     if((err = pthread_create(&tid, 0, control_thread, 0)))
-      fatal(err, "pthread_create control_thread");
+      disorder_fatal(err, "pthread_create control_thread");
   }
   if(dumpfile) {
     int fd;
@@ -695,22 +809,87 @@ int main(int argc, char **argv) {
     size_t written;
 
     if((fd = open(dumpfile, O_RDWR|O_TRUNC|O_CREAT, 0666)) < 0)
-      fatal(errno, "opening %s", dumpfile);
+      disorder_fatal(errno, "opening %s", dumpfile);
     /* Fill with 0s to a suitable size */
     memset(buffer, 0, sizeof buffer);
     for(written = 0; written < dump_size * sizeof(int16_t);
         written += sizeof buffer) {
       if(write(fd, buffer, sizeof buffer) < 0)
-        fatal(errno, "clearing %s", dumpfile);
+        disorder_fatal(errno, "clearing %s", dumpfile);
     }
     /* Map the buffer into memory for convenience */
     dump_buffer = mmap(0, dump_size * sizeof(int16_t), PROT_READ|PROT_WRITE,
                        MAP_SHARED, fd, 0);
     if(dump_buffer == (void *)-1)
-      fatal(errno, "mapping %s", dumpfile);
-    info("dumping to %s", dumpfile);
+      disorder_fatal(errno, "mapping %s", dumpfile);
+    disorder_info("dumping to %s", dumpfile);
+  }
+  /* Set up output.  Currently we only support L16 so there's no harm setting
+   * the format before we know what it is! */
+  uaudio_set_format(44100/*Hz*/, 2/*channels*/,
+                    16/*bits/channel*/, 1/*signed*/);
+  backend->start(playrtp_callback, NULL);
+  /* We receive and convert audio data in a background thread */
+  if((err = pthread_create(&ltid, 0, listen_thread, 0)))
+    disorder_fatal(err, "pthread_create listen_thread");
+  /* We have a second thread to add received packets to the queue */
+  if((err = pthread_create(&ltid, 0, queue_thread, 0)))
+    disorder_fatal(err, "pthread_create queue_thread");
+  pthread_mutex_lock(&lock);
+  time_t lastlog = 0;
+  for(;;) {
+    /* Wait for the buffer to fill up a bit */
+    playrtp_fill_buffer();
+    /* Start playing now */
+    disorder_info("Playing...");
+    next_timestamp = pheap_first(&packets)->timestamp;
+    active = 1;
+    pthread_mutex_unlock(&lock);
+    backend->activate();
+    pthread_mutex_lock(&lock);
+    /* Wait until the buffer empties out
+     *
+     * If there's a packet that we can play right now then we definitely
+     * continue.
+     *
+     * Also if there's at least minbuffer samples we carry on regardless and
+     * insert silence.  The assumption is there's been a pause but more data
+     * is now available.
+     */
+    while(nsamples >= minbuffer
+         || (nsamples > 0
+             && contains(pheap_first(&packets), next_timestamp))) {
+      if(monitor) {
+        time_t now = xtime(0);
+
+        if(now >= lastlog + 60) {
+          int offset = nsamples - minbuffer;
+          double offtime = (double)offset / (uaudio_rate * uaudio_channels);
+          disorder_info("%+d samples off (%d.%02ds, %d bytes)",
+                        offset,
+                        (int)fabs(offtime) * (offtime < 0 ? -1 : 1),
+                        (int)(fabs(offtime) * 100) % 100,
+                        offset * uaudio_bits / CHAR_BIT);
+          lastlog = now;
+        }
+      }
+      //fprintf(stderr, "%8u/%u (%u) PLAYING\n", nsamples, maxbuffer, minbuffer);
+      pthread_cond_wait(&cond, &lock);
+    }
+#if 0
+    if(nsamples) {
+      struct packet *p = pheap_first(&packets);
+      fprintf(stderr, "nsamples=%u (%u) next_timestamp=%"PRIx32", first packet is [%"PRIx32",%"PRIx32")\n",
+              nsamples, minbuffer, next_timestamp,p->timestamp,p->timestamp+p->nsamples);
+    }
+#endif
+    /* Stop playing for a bit until the buffer re-fills */
+    pthread_mutex_unlock(&lock);
+    backend->deactivate();
+    pthread_mutex_lock(&lock);
+    active = 0;
+    /* Go back round */
   }
-  play_rtp();
   return 0;
 }
 
index 900ff50..ff8a761 100644 (file)
@@ -58,11 +58,15 @@ struct packet {
    *
    * Valid values are:
    * - @ref IDLE - the idle bit was set in the RTP packet
+   * - @ref SILENT - packet is entirely silent
    */
   unsigned flags;
 /** @brief idle bit set in RTP packet*/
 #define IDLE 0x0001
 
+/** @brief RTP packet is entirely silent */
+#define SILENT 0x0002
+
   /** @brief Raw sample data
    *
    * Only the first @p nsamples samples are defined; the rest is uninitialized
@@ -128,7 +132,6 @@ void playrtp_free_packet(struct packet *p);
 void playrtp_fill_buffer(void);
 struct packet *playrtp_next_packet(void);
 
-extern const char *device;
 extern struct packet *received_packets;
 extern struct packet **received_tail;
 extern pthread_mutex_t receive_lock;
diff --git a/clients/resample.c b/clients/resample.c
new file mode 100644 (file)
index 0000000..bc41013
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "common.h"
+
+#include <unistd.h>
+#include <locale.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include "resample.h"
+#include "mem.h"
+#include "syscalls.h"
+#include "log.h"
+
+static int input_bits = 16;
+static int input_channels = 2;
+static int input_rate = 44100;
+static int input_signed = 1;
+static int input_endian = ENDIAN_NATIVE;
+static int output_bits = 16;
+static int output_channels = 2;
+static int output_rate = 44100;
+static int output_signed = 1;
+static int output_endian = ENDIAN_NATIVE;
+
+static const struct option options[] = {
+  { "help", no_argument, 0, 'h' },
+  { "input-bits", required_argument, 0, 'b' },
+  { "input-channels", required_argument, 0, 'c' },
+  { "input-rate", required_argument, 0, 'r' },
+  { "input-signed", no_argument, 0, 's' },
+  { "input-unsigned", no_argument, 0, 'u' },
+  { "input-endian", required_argument, 0, 'e' },
+  { "output-bits", required_argument, 0, 'B' },
+  { "output-channels", required_argument, 0, 'C' },
+  { "output-rate", required_argument, 0, 'R' },
+  { "output-signed", no_argument, 0, 'S' },
+  { "output-unsigned", no_argument, 0, 'U' },
+  { "output-endian", required_argument, 0, 'E' },
+  { 0, 0, 0, 0 }
+};
+
+/* display usage message and terminate */
+static void help(void) {
+  xprintf("Usage:\n"
+         "  resample [OPTIONS] < INPUT > OUTPUT\n"
+         "Options:\n"
+         "  --help, -h                      Display usage message\n"
+          "Input format:\n"
+         "  --input-bits, -b N              Bits/sample (16)\n"
+         "  --input-channels, -c N          Samples/frame (2)\n"
+         "  --input-rate, -r N              Frames/second (44100)\n"
+         "  --input-signed, -s              Signed samples (yes)\n"
+         "  --input-unsigned, -u            Unsigned samples\n"
+         "  --input-endian, -e big|little   Sample endianness (native)\n"
+          "Output format:\n"
+         "  --output-bits, -B N             Bits/sample (16)\n"
+         "  --output-channels, -C N         Samples/frame (2)\n"
+         "  --output-rate, -R N             Frames/second (44100)\n"
+         "  --output-signed, -S             Signed samples (yes)\n"
+         "  --output-unsigned, -U           Unsigned samples\n"
+         "  --output-endian, -E big|little  Sample endianness (native)\n"
+          "Defaults are in brackets.\n"
+          "\n"
+          "Feeds raw sample data through resample_convert().\n");
+  xfclose(stdout);
+  exit(0);
+}
+
+static void converted(uint8_t *bytes,
+                      size_t nbytes,
+                      void attribute((unused)) *cd) {
+  while(nbytes > 0) {
+    ssize_t n = write(1, bytes, nbytes);
+    if(n < 0)
+      disorder_fatal(errno, "writing to stdout");
+    bytes += n;
+    nbytes -= n;
+  }
+}
+
+int main(int argc, char **argv) {
+  int n;
+
+  mem_init();
+  if(!setlocale(LC_CTYPE, "")) 
+    disorder_fatal(errno, "error calling setlocale");
+  while((n = getopt_long(argc, argv, "+hb:c:r:sue:B:C:R:SUE:", 
+                         options, 0)) >= 0) {
+    switch(n) {
+    case 'h': help();
+    case 'b': input_bits = atoi(optarg); break;
+    case 'c': input_channels = atoi(optarg); break;
+    case 'r': input_rate = atoi(optarg); break;
+    case 's': input_signed = 1; break;
+    case 'u': input_signed = 1; break;
+    case 'e':
+      switch(optarg[0]) {
+      case 'b': case 'B': input_endian = ENDIAN_BIG; break;
+      case 'l': case 'L': input_endian = ENDIAN_LITTLE; break;
+      case 'n': case 'N': input_endian = ENDIAN_NATIVE; break;
+      default: disorder_fatal(0, "unknown endianness '%s'", optarg);
+      }
+      break;
+    case 'B': output_bits = atoi(optarg); break;
+    case 'C': output_channels = atoi(optarg); break;
+    case 'R': output_rate = atoi(optarg); break;
+    case 'S': output_signed = 1; break;
+    case 'U': output_signed = 1; break;
+    case 'E':
+      switch(optarg[0]) {
+      case 'b': case 'B': output_endian = ENDIAN_BIG; break;
+      case 'l': case 'L': output_endian = ENDIAN_LITTLE; break;
+      case 'n': case 'N': output_endian = ENDIAN_NATIVE; break;
+      default: disorder_fatal(0, "unknown endianness '%s'", optarg);
+      }
+      break;
+    default: disorder_fatal(0, "invalid option");
+    }
+  }
+  struct resampler rs[1];
+  resample_init(rs, input_bits, input_channels, input_rate, input_signed,
+                input_endian, output_bits, output_channels, output_rate,
+                output_signed, output_endian);
+#define BUFFER_SIZE (1024 * 1024)
+  uint8_t *buffer = xmalloc_noptr(BUFFER_SIZE);
+  size_t used = 0;
+  int eof = 0;
+
+  while(used || !eof) {
+    if(!eof) {
+      ssize_t r = read(0, buffer + used, BUFFER_SIZE - used);
+      if(r < 0)
+        disorder_fatal(errno, "reading from stdin");
+      if(r == 0)
+        eof = 1;
+      used += r;
+    }
+    size_t consumed = resample_convert(rs, buffer, used, eof, converted, 0);
+    memmove(buffer, buffer + consumed, used - consumed);
+    used -= consumed;
+  }
+  if(close(1) < 0)
+    disorder_fatal(errno, "closing stdout");
+  return 0;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/clients/rtpmon.c b/clients/rtpmon.c
new file mode 100644 (file)
index 0000000..32974e8
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file clients/rtpmon.c
+ * @brief RTP monitor
+ *
+ * This progam monitors the rate at which data arrives by RTP and
+ * constantly display it.  It is intended for debugging only.
+ *
+ * TODO de-dupe with playrtp.
+ */
+#include "common.h"
+
+#include <getopt.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <locale.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+
+#include "syscalls.h"
+#include "timeval.h"
+#include "mem.h"
+#include "log.h"
+#include "version.h"
+#include "addr.h"
+#include "configuration.h"
+#include "rtp.h"
+
+/** @brief Record of one packet */
+struct entry {
+  /** @brief When packet arrived */
+  struct timeval when;
+
+  /** @brief Serial number of first sample */
+  uint32_t serial;
+};
+
+/** @brief Bytes per frame */
+static unsigned bpf = 4;
+
+/** @brief Frame serial number */
+static uint32_t serial;
+
+/** @brief Size of ring buffer */
+#define RINGSIZE 131072
+
+/** @brief Ring buffer */
+static struct entry ring[RINGSIZE];
+
+/** @brief Where new packets join the ring */
+static unsigned ringtail;
+
+static const struct option options[] = {
+  { "help", no_argument, 0, 'h' },
+  { "version", no_argument, 0, 'V' },
+  { "bpf", required_argument, 0, 'b' },
+  { 0, 0, 0, 0 }
+};
+
+static void help(void) {
+  xprintf("Usage:\n"
+         "  rtpmon [OPTIONS] [ADDRESS] PORT\n"
+         "Options:\n"
+          "  --bpf, -b               Bytes/frame (default 4)\n"
+         "  --help, -h              Display usage message\n"
+         "  --version, -V           Display version number\n"
+          );
+  xfclose(stdout);
+  exit(0);
+}
+
+/** @brief Compute the rate by sampling at two points in the ring buffer */
+static double rate(unsigned earlier, unsigned later) {
+  const uint32_t frames = ring[later].serial - ring[earlier].serial;
+  const int64_t us = tvsub_us(ring[later].when, ring[earlier].when);
+
+  if(us)  
+    return 1000000.0 * frames / us;
+  else
+    return 0.0;
+}
+
+/** @brief Called to say we received some bytes
+ * @param when When we received them
+ * @param n How many frames of audio data we received
+ */
+static void frames(const struct timeval *when, size_t n) {
+  const time_t prev = ring[(ringtail - 1) % RINGSIZE].when.tv_sec;
+
+  ring[ringtail].when = *when;
+  ring[ringtail].serial = serial;
+  serial += n;
+  ringtail = (ringtail + 1) % RINGSIZE;
+  // Report once a second
+  if(prev != when->tv_sec) {
+    if(printf("%8.2f  %8.2f  %8.2f  %8.2f  %8.2f  %8.2f  %8.2f\n",
+              rate((ringtail - RINGSIZE / 128) % RINGSIZE,
+                   (ringtail - 1) % RINGSIZE),
+              rate((ringtail - RINGSIZE / 64) % RINGSIZE,
+                   (ringtail - 1) % RINGSIZE),
+              rate((ringtail - RINGSIZE / 32) % RINGSIZE,
+                   (ringtail - 1) % RINGSIZE),
+              rate((ringtail - RINGSIZE / 16) % RINGSIZE,
+                   (ringtail - 1) % RINGSIZE),
+              rate((ringtail - RINGSIZE / 8) % RINGSIZE,
+                   (ringtail - 1) % RINGSIZE),
+              rate((ringtail - RINGSIZE / 4) % RINGSIZE,
+                   (ringtail - 1) % RINGSIZE),
+              rate((ringtail - RINGSIZE / 2) % RINGSIZE,
+                   (ringtail - 1) % RINGSIZE)) < 0
+       || fflush(stdout) < 0)
+      disorder_fatal(errno, "stdout");
+  }
+}
+
+int main(int argc, char **argv) {
+  int n;
+  struct addrinfo *res;
+  struct stringlist sl;
+  struct ip_mreq mreq;
+  struct ipv6_mreq mreq6;
+  int rtpfd;
+  char *sockname;
+  int is_multicast;
+  union any_sockaddr {
+    struct sockaddr sa;
+    struct sockaddr_in in;
+    struct sockaddr_in6 in6;
+  };
+  union any_sockaddr mgroup;
+
+  static const struct addrinfo prefs = {
+    .ai_flags = AI_PASSIVE,
+    .ai_family = PF_INET,
+    .ai_socktype = SOCK_DGRAM,
+    .ai_protocol = IPPROTO_UDP
+  };
+  static const int one = 1;
+
+  mem_init();
+  if(!setlocale(LC_CTYPE, "")) 
+    disorder_fatal(errno, "error calling setlocale");
+  while((n = getopt_long(argc, argv, "hVb:", options, 0)) >= 0) {
+    switch(n) {
+    case 'h': help();
+    case 'V': version("rtpmon");
+    case 'b': bpf = atoi(optarg); break;
+    default: disorder_fatal(0, "invalid option");
+    }
+  }
+  argc -= optind;
+  argv += optind;
+  switch(argc) {
+  case 1:
+  case 2:
+    /* Use command-line ADDRESS+PORT or just PORT */
+    sl.n = argc;
+    sl.s = argv;
+    break;
+  default:
+    disorder_fatal(0, "usage: rtpmon [OPTIONS] [ADDRESS] PORT");
+  }
+  if(!(res = get_address(&sl, &prefs, &sockname)))
+    exit(1);
+  /* Create the socket */
+  if((rtpfd = socket(res->ai_family,
+                     res->ai_socktype,
+                     res->ai_protocol)) < 0)
+    disorder_fatal(errno, "error creating socket");
+  /* Allow multiple listeners */
+  xsetsockopt(rtpfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
+  is_multicast = multicast(res->ai_addr);
+  /* The multicast and unicast/broadcast cases are different enough that they
+   * are totally split.  Trying to find commonality between them causes more
+   * trouble that it's worth. */
+  if(is_multicast) {
+    /* Stash the multicast group address */
+    memcpy(&mgroup, res->ai_addr, res->ai_addrlen);
+    switch(res->ai_addr->sa_family) {
+    case AF_INET:
+      mgroup.in.sin_port = 0;
+      break;
+    case AF_INET6:
+      mgroup.in6.sin6_port = 0;
+      break;
+    default:
+      disorder_fatal(0, "unsupported family %d", (int)res->ai_addr->sa_family);
+    }
+    /* Bind to to the multicast group address */
+    if(bind(rtpfd, res->ai_addr, res->ai_addrlen) < 0)
+      disorder_fatal(errno, "error binding socket to %s",
+                     format_sockaddr(res->ai_addr));
+    /* Add multicast group membership */
+    switch(mgroup.sa.sa_family) {
+    case PF_INET:
+      mreq.imr_multiaddr = mgroup.in.sin_addr;
+      mreq.imr_interface.s_addr = 0;      /* use primary interface */
+      if(setsockopt(rtpfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                    &mreq, sizeof mreq) < 0)
+        disorder_fatal(errno, "error calling setsockopt IP_ADD_MEMBERSHIP");
+      break;
+    case PF_INET6:
+      mreq6.ipv6mr_multiaddr = mgroup.in6.sin6_addr;
+      memset(&mreq6.ipv6mr_interface, 0, sizeof mreq6.ipv6mr_interface);
+      if(setsockopt(rtpfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+                    &mreq6, sizeof mreq6) < 0)
+        disorder_fatal(errno, "error calling setsockopt IPV6_JOIN_GROUP");
+      break;
+    default:
+      disorder_fatal(0, "unsupported address family %d", res->ai_family);
+    }
+    /* Report what we did */
+    disorder_info("listening on %s multicast group %s",
+                  format_sockaddr(res->ai_addr), format_sockaddr(&mgroup.sa));
+  } else {
+    /* Bind to 0/port */
+    switch(res->ai_addr->sa_family) {
+    case AF_INET: {
+      struct sockaddr_in *in = (struct sockaddr_in *)res->ai_addr;
+      
+      memset(&in->sin_addr, 0, sizeof (struct in_addr));
+      if(bind(rtpfd, res->ai_addr, res->ai_addrlen) < 0)
+        disorder_fatal(errno, "error binding socket to 0.0.0.0 port %d",
+                       ntohs(in->sin_port));
+      break;
+    }
+    case AF_INET6: {
+      struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)res->ai_addr;
+      
+      memset(&in6->sin6_addr, 0, sizeof (struct in6_addr));
+      break;
+    }
+    default:
+      disorder_fatal(0, "unsupported family %d", (int)res->ai_addr->sa_family);
+    }
+    if(bind(rtpfd, res->ai_addr, res->ai_addrlen) < 0)
+      disorder_fatal(errno, "error binding socket to %s",
+                     format_sockaddr(res->ai_addr));
+    /* Report what we did */
+    disorder_info("listening on %s", format_sockaddr(res->ai_addr));
+  }
+  for(;;) {
+    struct rtp_header header;
+    char buffer[4096];
+    struct iovec iov[2];
+    struct timeval when;
+
+    iov[0].iov_base = &header;
+    iov[0].iov_len = sizeof header;
+    iov[1].iov_base = buffer;
+    iov[1].iov_len = sizeof buffer;
+    n = readv(rtpfd, iov, 2);
+    gettimeofday(&when, 0);
+    if(n < 0) {
+      switch(errno) {
+      case EINTR:
+        continue;
+      default:
+        disorder_fatal(errno, "error reading from socket");
+      }
+    }
+    if((size_t)n <= sizeof (struct rtp_header)) {
+      disorder_info("ignored a short packet");
+      continue;
+    }
+    frames(&when, (n - sizeof header) / bpf);
+  }
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index beac9c8..b0290c9 100644 (file)
@@ -1,7 +1,7 @@
 # Process this file with autoconf to produce a configure script.
 #
 # This file is part of DisOrder.
-# Copyright (C) 2004-2008 Richard Kettlewell
+# Copyright (C) 2004-2010 Richard Kettlewell
 # Portions copyright (C) 2007 Ross Younger
 #
 # This program is free software: you can redistribute it and/or modify
@@ -18,9 +18,9 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-AC_INIT([disorder], [4.3], [richard+disorder@sfere.greenend.org.uk])
+AC_INIT([disorder], [5.0], [richard+disorder@sfere.greenend.org.uk])
 AC_CONFIG_AUX_DIR([config.aux])
-AM_INIT_AUTOMAKE(disorder, [4.3])
+AM_INIT_AUTOMAKE(disorder, [5.0])
 AC_CONFIG_SRCDIR([server/disorderd.c])
 AM_CONFIG_HEADER([config.h])
 
@@ -39,8 +39,24 @@ want_alsa=yes
 want_oss=yes
 want_coreaudio=yes
 
+# By default we don't want gtk-osx.  But if you ask for --with-gtk-osx...
+#
+# Disobedience can be built to a native OS X application.  There are some
+# caveats:
+#  - you will still need Fink (or something) for other libraries
+#  - if you wanted an application bundle you are out of luck
+#  - the menu bar is still in the main window (not at the top of the screen)
+#
+# So it's still rather rough and ready.  But it does work...
+#
+# See:
+#   http://www.gtk-osx.org/
+#   http://live.gnome.org/GTK%2B/OSX/Framework
+want_gtkosx=no
+
 # Checks for programs.
 AC_PROG_CC
+AM_PROG_CC_C_O
 AC_PROG_AWK
 AC_SET_MAKE
 if test "x$GCC" = xyes; then
@@ -73,6 +89,10 @@ AC_ARG_WITH([tests],
            [AS_HELP_STRING([--without-tests],
                            [do not build test suit])],
            [want_tests=$withval])
+AC_ARG_WITH([gtk-osx],
+           [AS_HELP_STRING([--with-gtk-osx],
+                           [use native GTK+ (OS X only)])],
+           [want_gtkosx=$withval])
 
 AC_MSG_CHECKING([for a known target platform])
 case "$host" in
@@ -90,8 +110,11 @@ case "$host" in
   ;;
 *-apple-darwin* )
   AC_MSG_RESULT([Mac OS X])
+  # AudioHardwareGetProperty is deprecated in 10.6, but it's still _there_,
+  # so we'll just turn off the warning.
+  CC="$CC -Wno-deprecated-declarations"
   if test $want_coreaudio = yes; then
-    COREAUDIO="-framework CoreAudio"
+    COREAUDIO="-framework CoreFoundation -framework CoreServices -framework CoreAudio"
   fi
   browser=open
   AC_MSG_CHECKING([Mac OS X target version])
@@ -117,9 +140,11 @@ case "$host" in
   AC_DEFINE_UNQUOTED([MAC_OS_X_VERSION_MIN_REQUIRED], [$minver],
                      [define to minimum version of Mac OS X to support])
 
-  # Fink's GTK+ is hopelessly broken
-  PKG_CONFIG_PATH=${PKG_CONFIG_PATH:-/sw/lib/pango-ft219/lib/pkgconfig}
-  export PKG_CONFIG_PATH
+  if test $want_gtkosx = no; then
+    # Fink's GTK+ is hopelessly broken
+    PKG_CONFIG_PATH=${PKG_CONFIG_PATH:-/sw/lib/pango-ft219/lib/pkgconfig}
+    export PKG_CONFIG_PATH
+  fi
 
   ;;
 *-freebsd* )
@@ -261,7 +286,7 @@ if test $want_cgi = yes; then
         fi
       done
     ])
-    if test "$rjk_cv_cgiexecdir" = "not found"; then
+    if test "$rjk_cv_httpdir" = "not found"; then
       AC_MSG_ERROR([cannot identify httpd documentroot.  Set httpdir on configure command line])
     fi
     httpdir="$rjk_cv_httpdir"
@@ -301,6 +326,11 @@ if test -z "$pkghttpdir"; then
 fi
 AC_SUBST([pkghttpdir])
 
+if test -z "$dochtmldir"; then
+  dochtmldir='$(docdir)/html'
+fi
+AC_SUBST([dochtmldir])
+
 subdirs="scripts lib"
 if test $want_tests = yes; then
   subdirs="${subdirs} libtests"
@@ -308,7 +338,7 @@ fi
 subdirs="${subdirs} clients doc examples debian"
 
 if test $want_server = yes; then
-  subdirs="${subdirs} server plugins driver sounds"
+  subdirs="${subdirs} server plugins sounds"
 fi
 if test $want_cgi = yes; then
   subdirs="${subdirs} cgi templates images"
@@ -320,7 +350,7 @@ if test $want_gtk = yes; then
   fi
 fi
 if test $want_tests = yes && test $want_python = yes; then
-  AM_PATH_PYTHON([2.4])
+  AM_PATH_PYTHON([2.5])
   subdirs="${subdirs} python tests"
 fi
 AC_SUBST([subdirs])
@@ -359,16 +389,39 @@ AC_PATH_PROG([SENDMAIL],[sendmail],[none],[$PATH:/usr/sbin:/usr/lib])
 # Macs might have libraries under fink's root
 AC_PATH_PROG([FINK],[fink],[none],[$PATH:/sw/bin])
 if test "x$FINK" != xnone; then
+  # Find Fink prefix
   AC_CACHE_CHECK([fink install directory],[rjk_cv_finkprefix],[
     rjk_cv_finkprefix="`echo "$FINK" | sed 's,/bin/fink$,,'`"
   ])
+  # Add include and link paths
   finkdir="${rjk_cv_finkprefix}"
   finkbindir="${rjk_cv_finkprefix}/bin"
-  CPPFLAGS="${CPPFLAGS} -I${rjk_cv_finkprefix}/include/gc -I${rjk_cv_finkprefix}/include"
+  CPPFLAGS="${CPPFLAGS} -isystem ${rjk_cv_finkprefix}/include/gc -isystem ${rjk_cv_finkprefix}/include"
   if test $want_server = yes; then
-    CPPFLAGS="${CPPFLAGS} -I${rjk_cv_finkprefix}/include/db4"
+    CPPFLAGS="${CPPFLAGS} -isystem ${rjk_cv_finkprefix}/include/db4"
   fi
   LDFLAGS="${LDFLAGS} -L${rjk_cv_finkprefix}/lib"
+  # Distinguish 32- and 64-bit fink
+  AC_CACHE_CHECK([whether Fink is 32-bit or 64-bit],[rjk_cv_finkbits],[
+    odata=`otool -v -h $finkbindir/dpkg`
+    case "$odata" in
+    *X86_64* )
+      rjk_cv_finkbits=64
+      ;;
+    *I386* )
+      rjk_cv_finkbits=32
+      ;;
+    * )
+      rjk_cv_finkbits=unknown
+      ;;
+    esac
+  ])
+  # Match Fink so we can use its libraries
+  case $rjk_cv_finkbits in
+  32 | 64 )
+    CC="$CC -m$rjk_cv_finkbits"
+    ;;
+  esac
 else
   finkbindir=""
 fi
@@ -390,6 +443,8 @@ if test $want_alsa = yes; then
   AC_CHECK_LIB([asound], [snd_pcm_open],
                [AC_SUBST(LIBASOUND,[-lasound])])
 fi
+AC_CHECK_LIB([samplerate],[src_new],
+             [AC_SUBST([LIBSAMPLERATE],[-lsamplerate])])
 if test $want_server = yes; then
   RJK_CHECK_LIB(db, db_create, [#include <db.h>],
               [AC_SUBST(LIBDB,[-ldb])],
@@ -416,8 +471,14 @@ AC_CHECK_LIB([pthread], [pthread_create],
             [missing_libraries="$missing_libraries libpthread"])
 
 if test $want_gtk = yes; then
-  AM_PATH_GLIB_2_0([],[],[missing_libraries="$missing_libraries libglib"])
-  AM_PATH_GTK_2_0([],[],[missing_libraries="$missing_libraries libgtk"])
+  if test $want_gtkosx = yes; then
+    GTK_CFLAGS="-isystem /Library/Frameworks/Gtk.framework/Headers -isystem /Library/Frameworks/Cairo.framework/Headers"
+    GLIB_CFLAGS="-isystem /Library/Frameworks/GLib.framework/Headers"
+    GTK_LIBS="-framework Gtk -framework GLib"
+  else
+    AM_PATH_GLIB_2_0([],[],[missing_libraries="$missing_libraries libglib"])
+    AM_PATH_GTK_2_0([],[],[missing_libraries="$missing_libraries libgtk"])
+  fi
 fi
 
 # Some platforms have iconv already
@@ -469,6 +530,7 @@ AC_CHECK_HEADERS([dlfcn.h gcrypt.h \
                 syslog.h unistd.h],[:],[
   missing_headers="$missing_headers $ac_header"
 ])
+AC_CHECK_HEADERS([samplerate.h])
 
 if test ! -z "$missing_headers"; then
   AC_MSG_ERROR([missing headers:$missing_headers])
@@ -623,6 +685,7 @@ AM_CONDITIONAL([SERVER], [test x$want_server = xyes])
 if test $want_gtk = yes; then
   AC_DEFINE([WITH_GTK], [1], [define if using GTK+])
 fi
+AM_CONDITIONAL([GTK], [test x$want_gtk = xyes])
 
 if test "x$GCC" = xyes; then
   # We need LLONG_MAX and annoyingly GCC doesn't always give it to us
@@ -669,6 +732,25 @@ if test "x$GCC" = xyes; then
     gcc_werror=''
   fi
 
+  AC_CACHE_CHECK([checking for excessively strict -Wreturn-type],
+                 [rjk_cv_gcc44_stupidity],[
+    old_CC="$CC"
+    if test $GCC = yes; then
+      CC="$CC -Wreturn-type -Werror"
+    fi
+    AC_COMPILE_IFELSE([
+      static void *threadfn(void) {
+        for(;;)
+          ;
+      }],
+      [rjk_cv_gcc44_stupidity=no],
+      [rjk_cv_gcc44_stupidity=yes])
+    CC="$old_CC"
+  ])
+  if test $rjk_cv_gcc44_stupidity = yes; then
+    AC_DEFINE([HAVE_STUPID_GCC44],[1],[Define if your compiler has excessively strict -Wreturn-type])
+  fi
+
   # a reasonable default set of warnings
   CC="${CC} -Wall -W -Wpointer-arith -Wbad-function-cast \
        -Wwrite-strings -Wmissing-prototypes \
@@ -747,10 +829,10 @@ AC_CONFIG_FILES([Makefile
                 cgi/Makefile
                 clients/Makefile
                 disobedience/Makefile
+                disobedience/manual/Makefile
                 doc/Makefile
                 templates/Makefile
                 plugins/Makefile
-                driver/Makefile
                 debian/Makefile
                 sounds/Makefile
                 python/Makefile
@@ -765,6 +847,9 @@ fi
 if test $want_python = no; then
   AC_MSG_WARN([cannot run the test suit without Python])
 fi
+if test $want_server = yes && test "$ac_cv_lib_samplerate_src_new" != yes; then
+  AC_MSG_WARN([libsamplerate will be required in a future version])
+fi
 
 # Local Variables:
 # indent-tabs-mode:nil
index b4af06d..293df22 100644 (file)
@@ -22,7 +22,7 @@ EXTRA_DIST=README.Debian config.disorder-server control \
           copyright README.RTP \
           etc.disorder.options.user etc.disorder.config \
           postinst.disorder-server prerm.disorder-server \
-          postrm.disorder-server \
+          postrm.disorder-server overrides.disorder-server \
           templates.disorder-server conffiles.disorder-server \
           rules changelog usr.share.menu.disobedience \
-          postinst.disobedience
+          postinst.disobedience disobedience-manual
index 61e1fe7..e76e763 100644 (file)
@@ -1,3 +1,9 @@
+disorder (5.0) unstable; urgency=low
+
+  * DisOrder 5.0
+
+ -- Richard Kettlewell <rjk@greenend.org.uk>  Sun, 06 Jun 2010 12:43:21 +0100
+
 disorder (4.3) unstable; urgency=low
 
   * DisOrder 4.3
index f171349..2254137 100644 (file)
@@ -2,8 +2,8 @@ Source: disorder
 Maintainer: Richard Kettlewell <rjk@greenend.org.uk>
 Section: sound
 Priority: optional
-Standards-Version: 3.7.2.2
-Build-Depends: libgc6-dev | libgc-dev, libgcrypt-dev, libdb4.3-dev, libpcre3-dev, libvorbis-dev, libmad0-dev, libasound2-dev, libao-dev, python, libflac-dev, libgtk2.0-dev
+Standards-Version: 3.8.1.0
+Build-Depends: libgc6-dev | libgc-dev, libgcrypt-dev, libdb4.3-dev, libpcre3-dev, libvorbis-dev, libmad0-dev, libasound2-dev, libao-dev, python, libflac-dev, libgtk2.0-dev (>= 2.12.12)
 Vcs-Bzr: http://www.greenend.org.uk/rjk/bzr/disorder.dev/
 Homepage: http://www.greenend.org.uk/rjk/disorder/
 
index 47a18e5..918dbb4 100644 (file)
@@ -11,6 +11,8 @@ Portions copyright (C) 2007 Mark Wooding
 Portions extracted from MPG321, http://mpg321.sourceforge.net/
   Copyright (C) 2001 Joe Drew
   Copyright (C) (C) 2000-2001 Robert Leslie
+Portions Copyright (C) 1997-2006 Free Software Foundation, Inc.
+Portions Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
 Binaries may derive extra copyright owners through linkage
 (binary distributors are expected to do their own legwork)
 
@@ -27,5 +29,5 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-On Debian systems, look in /usr/share/common-licenses/GPL for a copy
+On Debian systems, look in /usr/share/common-licenses/GPL-3 for a copy
 of the GPL.
diff --git a/debian/disobedience-manual b/debian/disobedience-manual
new file mode 100644 (file)
index 0000000..93a81b3
--- /dev/null
@@ -0,0 +1,12 @@
+Document: disobedience-manual
+Title: Disobedience Manual
+Author: Richard Kettlewell
+Abstract: This manual describes how to use Disobedience, the GUI
+ client for DisOrder.
+Section: Sound
+
+Format: HTML
+Index: /usr/share/doc/disorder/html/index.html
+Files: /usr/share/doc/disorder/html/*.html
+ /usr/share/doc/disorder/html/*.png
+ /usr/share/doc/disorder/html/*.css
diff --git a/debian/overrides.disorder-server b/debian/overrides.disorder-server
new file mode 100644 (file)
index 0000000..404841e
--- /dev/null
@@ -0,0 +1,3 @@
+# No, we can't use '/etc/init.d/disorder stop', the whole point is to
+# avoid problems with that not working.
+disorder-server: start-stop-daemon-in-maintainer-script prerm:29
index 4c06bef..fc26afa 100644 (file)
@@ -21,7 +21,7 @@ set -e
 
 case "$1" in
 configure )
-  if type update-menus >/dev/null 2>&1; then
+  if command -v update-menus >/dev/null 2>&1; then
     update-menus
   fi
   ;;
index cb5e60d..316f1e9 100755 (executable)
@@ -1,7 +1,7 @@
 #! /bin/sh
 #
 # This file is part of DisOrder
-# Copyright (C) 2004, 2007, 2008 Richard Kettlewell
+# Copyright (C) 2004, 2007-2009 Richard Kettlewell
 #
 # 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
@@ -34,10 +34,31 @@ configure_init_d() {
 }
 
 restart_server() {
+  local first
+  local time
   invoke-rc.d disorder restart
-  # Wait for the server to get going.  This is a horrid bodge and ought
-  # to be done away with, but is required for the time being.  Sorry.
-  sleep 5
+  # Wait for the server to start
+  first=true
+  time=0
+  while :; do
+    if disorder version >/dev/null 2>&1; then
+      break
+    else
+      if [ $time -gt 30 ]; then
+        echo
+        echo "Server did not stabilise in a reasonable amount of time"
+        exit 1
+      fi
+      if $first; then
+       printf "Waiting for server to stabilise"
+       first=false
+      fi
+      printf "."
+      sleep 1
+    fi
+    time=`expr $time + 1`
+  done
+  echo
 }
 
 setup_guest() {
index 85f80a4..0742696 100755 (executable)
@@ -21,7 +21,7 @@ set -e
 
 case "$1" in
 remove )
-  if type update-menus >/dev/null 2>&1; then
+  if command -v update-menus >/dev/null 2>&1; then
     update-menus
   fi
   ;;
index 7ba0933..5aa1df6 100755 (executable)
@@ -30,6 +30,7 @@ cleanup_remove() {
   rm -f $state/__db.*
   rm -f $state/noticed.db
   rm -f $state/search.db
+  rm -f $state/isearch.db
   rm -f $state/tags.db
   rm -f $state/tracks.db
 }
@@ -43,6 +44,7 @@ cleanup_purge() {
   rm -f $state/prefs.db
   rm -f $state/schedule.db
   rm -f $state/users.db
+  rm -f $state/playlists.db
 }
 
 case "$1" in
@@ -54,7 +56,7 @@ purge )
     . /usr/share/debconf/confmodule
     db_purge
   fi
-  update-rc.d disorder remove
+  update-rc.d disorder remove > /dev/null
   cleanup_remove
   cleanup_purge
   ;;
index 5c53f50..1861c13 100755 (executable)
@@ -60,12 +60,14 @@ SENDMAIL=/usr/sbin/sendmail
 LIBTOOL=./libtool
 FAKEROOT=fakeroot
 
-# ./prepare is the script that generates configure etc.  It only needs to be
+SHELL=bash
+
+# ./autogen.sh is the script that generates configure etc.  It only needs to be
 # run if building from a checkout rather than a tarball.
 build:
        @set -e;if test ! -f configure; then \
-         echo ./prepare;\
-         ./prepare;\
+         echo ./autogen.sh;\
+         ./autogen.sh;\
        fi
        @set -e;if test ! -f config.status; then \
          echo ./configure ${CONFIGURE} ${CONFIGURE_EXTRA};\
@@ -85,8 +87,8 @@ pkg-disorder: build
        $(INSTALL_DATA) debian/copyright \
                debian/disorder/usr/share/doc/disorder/copyright
        $(INSTALL_DATA) debian/changelog \
-               debian/disorder/usr/share/doc/disorder/changelog.Debian
-       gzip -9 debian/disorder/usr/share/doc/disorder/changelog.Debian
+               debian/disorder/usr/share/doc/disorder/changelog
+       gzip -9 debian/disorder/usr/share/doc/disorder/changelog*
        @for f in conffiles templates; do\
          if test -e debian/$$f.disorder; then\
            echo $(INSTALL_DATA) debian/$$f.disorder debian/disorder/DEBIAN/$$f; \
@@ -101,6 +103,8 @@ pkg-disorder: build
        done
        $(MAKE) DESTDIR=`pwd`/debian/disorder installdirs install -C doc
        $(MAKE) DESTDIR=`pwd`/debian/disorder installdirs install -C clients
+       strip --remove-section=.comment debian/disorder/usr/bin/disorder
+       strip --remove-section=.comment debian/disorder/usr/bin/disorderfm
        $(MAKE) DESTDIR=`pwd`/debian/disorder installdirs install -C lib
        $(INSTALL_DATA) scripts/completion.bash \
                debian/disorder/etc/bash_completion.d/disorder
@@ -125,6 +129,10 @@ pkg-disorder: build
                 debian/disorder/usr/share/doc/disorder/README.* \
                 debian/disorder/usr/share/doc/disorder/BUGS \
                 debian/disorder/usr/share/man/man*/*
+       cd debian/disorder && \
+               find -name DEBIAN -prune -o -type f -print \
+                       | sed 's/^\.\///' \
+                       | xargs md5sum > DEBIAN/md5sums
        dpkg-gencontrol -isp -pdisorder -Pdebian/disorder -Tdebian/substvars.disorder
        chown -R root:root debian/disorder
        chmod -R g-ws debian/disorder
@@ -135,11 +143,14 @@ pkg-disorder-server: build
        $(MKDIR) debian/disorder-server
        $(MKDIR) debian/disorder-server/DEBIAN
        $(MKDIR) debian/disorder-server/usr/share/doc/disorder-server
+       $(MKDIR) debian/disorder-server/usr/share/lintian/overrides
+       $(INSTALL_DATA) debian/overrides.disorder-server \
+           debian/disorder-server/usr/share/lintian/overrides/disorder-server
        $(INSTALL_DATA) debian/copyright \
                debian/disorder-server/usr/share/doc/disorder-server/copyright
        $(INSTALL_DATA) debian/changelog \
-               debian/disorder-server/usr/share/doc/disorder-server/changelog.Debian
-       gzip -9 debian/disorder-server/usr/share/doc/disorder-server/changelog.Debian
+               debian/disorder-server/usr/share/doc/disorder-server/changelog
+       gzip -9 debian/disorder-server/usr/share/doc/disorder-server/changelog*
        @for f in preinst postinst prerm postrm config; do\
          if test -e debian/$$f.disorder-server; then\
            echo $(INSTALL_SCRIPT) debian/$$f.disorder-server debian/disorder-server/DEBIAN/$$f; \
@@ -155,7 +166,6 @@ pkg-disorder-server: build
        $(MAKE) DESTDIR=`pwd`/debian/disorder-server installdirs install -C images
        $(MAKE) DESTDIR=`pwd`/debian/disorder-server installdirs install -C server
        $(MAKE) DESTDIR=`pwd`/debian/disorder-server installdirs install -C templates
-       $(MAKE) DESTDIR=`pwd`/debian/disorder-server installdirs install -C driver
        $(MAKE) DESTDIR=`pwd`/debian/disorder-server installdirs install -C plugins
        $(MAKE) DESTDIR=`pwd`/debian/disorder-server installdirs install -C sounds
        $(MAKE) DESTDIR=`pwd`/debian/disorder-server installdirs install -C doc
@@ -170,10 +180,6 @@ pkg-disorder-server: build
          echo mv $$f $${f/.0.0.0};\
          mv $$f $${f/.0.0.0};\
        done
-       @for f in debian/disorder-server/usr/lib/ao/plugins*/*.so.0.0.0; do \
-         echo mv $$f $${f/.0.0.0};\
-         mv $$f $${f/.0.0.0};\
-       done
        find debian/disorder-server -name '*.so' -print0 | xargs -r0 strip --strip-unneeded
        find debian/disorder-server -name '*.so' -print0 | xargs -r0 chmod -x
        $(MKDIR) debian/disorder-server/etc/disorder
@@ -197,6 +203,14 @@ pkg-disorder-server: build
                debian/disorder-server/usr/lib/disorder/*.so
        rm -rf debian/disorder-server/usr/share/doc/disorder-server
        ln -s disorder debian/disorder-server/usr/share/doc/disorder-server
+       strip --remove-section=.comment \
+               debian/disorder-server/usr/sbin/* \
+               debian/disorder-server${cgiexecdir}/* \
+               debian/disorder-server/usr/lib/disorder/*.so
+       cd debian/disorder-server && \
+               find -name DEBIAN -prune -o -type f -print \
+                       | sed 's/^\.\///' \
+                       | xargs md5sum > DEBIAN/md5sums
        dpkg-gencontrol -isp -pdisorder-server -Pdebian/disorder-server -Tdebian/substvars.disorder-server
        chown -R root:root debian/disorder-server
        chmod -R g-ws debian/disorder-server
@@ -210,8 +224,8 @@ pkg-disorder-playrtp: build
        $(INSTALL_DATA) debian/copyright \
                debian/disorder-playrtp/usr/share/doc/disorder-playrtp/copyright
        $(INSTALL_DATA) debian/changelog \
-               debian/disorder-playrtp/usr/share/doc/disorder-playrtp/changelog.Debian
-       gzip -9 debian/disorder-playrtp/usr/share/doc/disorder-playrtp/changelog.Debian
+               debian/disorder-playrtp/usr/share/doc/disorder-playrtp/changelog
+       gzip -9 debian/disorder-playrtp/usr/share/doc/disorder-playrtp/changelog*
        @for f in preinst postinst prerm postrm conffiles templates config; do\
          if test -e debian/$$f.disorder-playrtp; then\
            echo $(INSTALL_SCRIPT) debian/$$f.disorder-playrtp debian/disorder-playrtp/DEBIAN/$$f; \
@@ -222,6 +236,7 @@ pkg-disorder-playrtp: build
        $(MKDIR) debian/disorder-playrtp/usr/share/man/man1
        $(INSTALL_PROGRAM) clients/disorder-playrtp \
                debian/disorder-playrtp/usr/bin/disorder-playrtp
+       strip --remove-section=.comment debian/disorder-playrtp/usr/bin/disorder-playrtp
        $(INSTALL_DATA) doc/disorder-playrtp.1 \
                debian/disorder-playrtp/usr/share/man/man1/disorder-playrtp.1
        dpkg-shlibdeps -Tdebian/substvars.disorder-playrtp \
@@ -230,6 +245,10 @@ pkg-disorder-playrtp: build
                debian/disorder-playrtp/usr/share/doc/disorder-playrtp/README
        $(INSTALL_DATA) CHANGES.html debian/disorder-playrtp/usr/share/doc/disorder-playrtp/CHANGES.html
        gzip -9f debian/disorder-playrtp/usr/share/man/man*/*
+       cd debian/disorder-playrtp && \
+               find -name DEBIAN -prune -o -type f -print \
+                       | sed 's/^\.\///' \
+                       | xargs md5sum > DEBIAN/md5sums
        dpkg-gencontrol -isp -pdisorder-playrtp -Pdebian/disorder-playrtp -Tdebian/substvars.disorder-playrtp
        chown -R root:root debian/disorder-playrtp
        chmod -R g-ws debian/disorder-playrtp
@@ -243,8 +262,8 @@ pkg-disobedience: build
        $(INSTALL_DATA) debian/copyright \
                debian/disobedience/usr/share/doc/disobedience/copyright
        $(INSTALL_DATA) debian/changelog \
-               debian/disobedience/usr/share/doc/disobedience/changelog.Debian
-       gzip -9 debian/disobedience/usr/share/doc/disobedience/changelog.Debian
+               debian/disobedience/usr/share/doc/disobedience/changelog
+       gzip -9 debian/disobedience/usr/share/doc/disobedience/changelog*
        @for f in preinst postinst prerm postrm conffiles templates config; do\
          if test -e debian/$$f.disobedience; then\
            echo $(INSTALL_SCRIPT) debian/$$f.disobedience debian/disobedience/DEBIAN/$$f; \
@@ -255,7 +274,9 @@ pkg-disobedience: build
        $(MKDIR) debian/disobedience/usr/share/man/man1
        $(MKDIR) debian/disobedience/usr/share/pixmaps
        $(MKDIR) debian/disobedience/usr/share/menu
+       $(MKDIR) debian/disobedience/usr/share/doc-base
        $(MAKE) -C disobedience install DESTDIR=`pwd`/debian/disobedience
+       strip --remove-section=.comment debian/disobedience/usr/bin/disobedience
        $(INSTALL_DATA) doc/disobedience.1 \
                debian/disobedience/usr/share/man/man1/disobedience.1
        $(INSTALL_DATA) images/disobedience16x16.xpm \
@@ -263,11 +284,17 @@ pkg-disobedience: build
                        debian/disobedience/usr/share/pixmaps
        $(INSTALL_DATA) debian/usr.share.menu.disobedience \
                debian/disobedience/usr/share/menu/disobedience
+       $(INSTALL_DATA) debian/disobedience-manual \
+               debian/disobedience/usr/share/doc-base/disobedience-manual
        gzip -9f debian/disobedience/usr/share/man/man*/*
        dpkg-shlibdeps -Tdebian/substvars.disobedience \
                debian/disobedience/usr/bin/*
        rm -rf debian/disobedience/usr/share/doc/disobedience
        ln -s disorder debian/disobedience/usr/share/doc/disobedience
+       cd debian/disobedience && \
+               find -name DEBIAN -prune -o -type f -print \
+                       | sed 's/^\.\///' \
+                       | xargs md5sum > DEBIAN/md5sums
        dpkg-gencontrol -isp -pdisobedience -Pdebian/disobedience -Tdebian/substvars.disobedience
        chown -R root:root debian/disobedience
        chmod -R g-ws debian/disobedience
index e7072c7..7b823c2 100644 (file)
@@ -1,6 +1,6 @@
 #
 # This file is part of DisOrder.
-# Copyright (C) 2006-2008 Richard Kettlewell
+# Copyright (C) 2006-2010 Richard Kettlewell
 #
 # 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
 #
 
 bin_PROGRAMS=disobedience
-pkgdata_DATA=disobedience.html
+SUBDIRS=manual
 
 AM_CPPFLAGS=-I${top_srcdir}/lib -I../lib
 AM_CFLAGS=$(GLIB_CFLAGS) $(GTK_CFLAGS)
-PNGS:=$(shell export LC_COLLATE=C;echo ${top_srcdir}/images/*.png)
 
 disobedience_SOURCES=disobedience.h disobedience.c client.c queue.c    \
        recent.c added.c queue-generic.c queue-generic.h queue-menu.c   \
        choose.c choose-menu.c choose-search.c popup.c misc.c           \
        control.c properties.c menu.c log.c progress.c login.c rtp.c    \
        help.c ../lib/memgc.c settings.c users.c lookup.c choose.h      \
-       popup.h
+       popup.h playlists.c multidrag.c multidrag.h autoscroll.c        \
+       autoscroll.h
 disobedience_LDADD=../lib/libdisorder.a $(LIBPCRE) $(LIBGC) $(LIBGCRYPT) \
-       $(LIBASOUND) $(COREAUDIO) $(LIBDB)
+       $(LIBASOUND) $(COREAUDIO) $(LIBDB) $(LIBICONV)
 disobedience_LDFLAGS=$(GTK_LIBS)
 
 install-exec-hook:
@@ -38,35 +38,13 @@ install-exec-hook:
 
 check: check-help
 
-disobedience.html: ../doc/disobedience.1 $(top_srcdir)/scripts/htmlman
-       rm -f $@.new
-       $(top_srcdir)/scripts/htmlman $< >$@.new
-       chmod 444 $@.new
-       mv -f $@.new $@
-
-misc.o: images.h
-
-images.h: $(PNGS)
-       set -e;                                                         \
-       exec > @$.new;                                                  \
-       for png in $(PNGS); do                                          \
-         name=`echo $$png | $(GNUSED) 's,.*/,,;s,\.png,,;'`;           \
-         gdk-pixbuf-csource --raw --name=image_$$name $$png;           \
-       done;                                                           \
-       echo "static const struct image images[] = {";                  \
-       for png in $(PNGS); do                                          \
-         name=`echo $$png | $(GNUSED) 's,.*/,,;s,\.png,,;'`;           \
-         echo "  { \"$$name.png\", image_$$name },";                   \
-       done;                                                           \
-       echo "};"
-       mv @$.new $@
+misc.o: ../images/images.h
 
 # check everything has working --help
 check-help: all
        unset DISPLAY;./disobedience --version > /dev/null
        unset DISPLAY;./disobedience --help > /dev/null
 
-CLEANFILES=disobedience.html images.h \
-          *.gcda *.gcov *.gcno *.c.html index.html
+CLEANFILES=*.gcda *.gcov *.gcno *.c.html index.html
 
 export GNUSED
index 32628ac..04e1d77 100644 (file)
@@ -66,7 +66,7 @@ static void added_changed(const char attribute((unused)) *event,
 }
 
 /** @brief Called at startup */
-static void added_init(void) {
+static void added_init(struct queuelike attribute((unused)) *ql) {
   event_register("rescan-complete", added_changed, 0);
 }
 
@@ -80,10 +80,10 @@ static const struct queue_column added_columns[] = {
 
 /** @brief Pop-up menu for new tracks list */
 static struct menuitem added_menuitems[] = {
-  { "Track properties", ql_properties_activate, ql_properties_sensitive, 0, 0 },
-  { "Play track", ql_play_activate, ql_play_sensitive, 0, 0 },
-  { "Select all tracks", ql_selectall_activate, ql_selectall_sensitive, 0, 0 },
-  { "Deselect all tracks", ql_selectnone_activate, ql_selectnone_sensitive, 0, 0 },
+  { "Track properties", GTK_STOCK_PROPERTIES, ql_properties_activate, ql_properties_sensitive, 0, 0 },
+  { "Play track", GTK_STOCK_MEDIA_PLAY, ql_play_activate, ql_play_sensitive, 0, 0 },
+  { "Select all tracks", GTK_STOCK_SELECT_ALL, ql_selectall_activate, ql_selectall_sensitive, 0, 0 },
+  { "Deselect all tracks", NULL, ql_selectnone_activate, ql_selectnone_sensitive, 0, 0 },
 };
 
 struct queuelike ql_added = {
@@ -93,6 +93,8 @@ struct queuelike ql_added = {
   .ncolumns = sizeof added_columns / sizeof *added_columns,
   .menuitems = added_menuitems,
   .nmenuitems = sizeof added_menuitems / sizeof *added_menuitems,
+  .drag_source_targets = choose_targets,
+  .drag_source_actions = GDK_ACTION_COPY,
 };
 
 GtkWidget *added_widget(void) {
diff --git a/disobedience/autoscroll.c b/disobedience/autoscroll.c
new file mode 100644 (file)
index 0000000..2b24699
--- /dev/null
@@ -0,0 +1,127 @@
+/* Derived from gtktreeview.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ * Portions copyright (C) 2009 Richard Kettlewell
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/** @file disobedience/autoscroll.c
+ * @brief Automatic scrolling of a GtkTreeView
+ *
+ * GTK+ doesn't expose the automatic scrolling support if you don't use its
+ * high-level treeview drag+drop features.
+ *
+ * Adapted from GTK+ upstream as of commit
+ * 7fda8e6378d90bc8cf670ffe9dea682911e5e241.
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "autoscroll.h"
+
+/** @brief Object data key used to track the autoscroll timeout */
+#define AUTOSCROLL_KEY "autoscroll.greenend.org.uk"
+
+/** @brief Controls size of edge region that provokes scrolling
+ *
+ * Actually this is half the size of the scroll region.  In isolation this may
+ * seem bizarre, but GTK+ uses the value internally for other purposes.
+ */
+#define SCROLL_EDGE_SIZE 15
+
+/** @brief Called from time to time to check whether auto-scrolling is needed
+ * @param data The GtkTreeView
+ * @return TRUE, to keep on truckin'
+ */
+static gboolean autoscroll_timeout(gpointer data) {
+  GtkTreeView *tree_view = data;
+  GdkRectangle visible_rect;
+  gint wx, wy, tx, ty;
+  gint offset;
+  gfloat value;
+
+  /* First we must find the pointer Y position in tree coordinates.  GTK+
+   * natively knows what the bin window is and can get the pointer in bin
+   * coords and convert to tree coords.  But there is no published way for us
+   * to find the bin window, so we must start in widget coords. */
+  gtk_widget_get_pointer(GTK_WIDGET(tree_view), &wx, &wy);
+  //fprintf(stderr, "widget coords: %d, %d\n", wx, wy);
+  gtk_tree_view_convert_widget_to_tree_coords(tree_view, wx, wy, &tx, &ty);
+  //fprintf(stderr, "tree coords: %d, %d\n", tx, ty);
+
+  gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
+
+  /* see if we are near the edge. */
+  offset = ty - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
+  if (offset > 0) {
+    offset = ty - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
+    if (offset < 0)
+      return TRUE;
+  }
+
+  GtkAdjustment *vadjustment = gtk_tree_view_get_vadjustment(tree_view);
+
+  value = CLAMP (vadjustment->value + offset, 0.0,
+                vadjustment->upper - vadjustment->page_size);
+  gtk_adjustment_set_value (vadjustment, value);
+
+  return TRUE;
+}
+
+/** @brief Enable autoscrolling
+ * @param tree_view Tree view to enable autoscrolling
+ *
+ * It's harmless to call this if autoscrolling is already enabled.
+ *
+ * It's up to you to cancel the callback when no longer required (including
+ * object destruction).
+ */
+void autoscroll_add(GtkTreeView *tree_view) {
+  guint *scrolldata = g_object_get_data(G_OBJECT(tree_view), AUTOSCROLL_KEY);
+  if(!scrolldata) {
+    /* Set up the callback */
+    scrolldata = g_new(guint, 1);
+    *scrolldata = gdk_threads_add_timeout(150, autoscroll_timeout, tree_view);
+    g_object_set_data(G_OBJECT(tree_view), AUTOSCROLL_KEY, scrolldata);
+    //fprintf(stderr, "autoscroll enabled\n");
+  }
+}
+
+/** @brief Disable autoscrolling
+ * @param tree_view Tree view to enable autoscrolling
+ *
+ * It's harmless to call this if autoscrolling is not enabled.
+ */
+void autoscroll_remove(GtkTreeView *tree_view) {
+  guint *scrolldata = g_object_get_data(G_OBJECT(tree_view), AUTOSCROLL_KEY);
+  if(scrolldata) {
+    g_object_set_data(G_OBJECT(tree_view), AUTOSCROLL_KEY, NULL);
+    g_source_remove(*scrolldata);
+    g_free(scrolldata);
+    //fprintf(stderr, "autoscroll disabled\n");
+  }
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/disobedience/autoscroll.h b/disobedience/autoscroll.h
new file mode 100644 (file)
index 0000000..0c0b28c
--- /dev/null
@@ -0,0 +1,38 @@
+/* Derived from gtktreeview.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ * Portions copyright (C) 2009 Richard Kettlewell
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/** @file disobedience/autoscroll.h
+ * @brief Automatic scrolling of a GtkTreeView
+ */
+#ifndef AUTOSCROLL_H
+#define AUTOSCROLL_H
+
+void autoscroll_add(GtkTreeView *tree_view);
+void autoscroll_remove(GtkTreeView *tree_view);
+
+#endif /* AUTOSCROLL_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index f1aa3b0..b0f59b5 100644 (file)
 #include "popup.h"
 #include "choose.h"
 
+static void choose_playchildren_callback(GtkTreeModel *model,
+                                         GtkTreePath *path,
+                                         GtkTreeIter *iter,
+                                         gpointer data);
+static void choose_playchildren_received(void *v,
+                                         const char *err,
+                                         int nvec, char **vec);
+static void choose_playchildren_played(void *v, const char *err);
+
 /** @brief Popup menu */
 static GtkWidget *choose_menu;
 
@@ -121,7 +130,7 @@ static void choose_properties_activate(GtkMenuItem attribute((unused)) *item,
   gtk_tree_selection_selected_foreach(choose_selection,
                                       choose_gather_selected_files_callback,
                                       v);
-  properties(v->nvec, (const char **)v->vec);
+  properties(v->nvec, (const char **)v->vec, toplevel);
 }
 
 /** @brief Set sensitivity for select children
@@ -210,10 +219,53 @@ static void choose_selectchildren_activate
                                       0);
 }
 
+/** @brief Play all children */
+static void choose_playchildren_activate
+    (GtkMenuItem attribute((unused)) *item,
+     gpointer attribute((unused)) userdata) {
+  /* Only one thing is selected */
+  gtk_tree_selection_selected_foreach(choose_selection,
+                                      choose_playchildren_callback,
+                                      0);
+}
+
+static void choose_playchildren_callback(GtkTreeModel attribute((unused)) *model,
+                                         GtkTreePath *path,
+                                         GtkTreeIter *iter,
+                                         gpointer attribute((unused)) data) {
+  /* Find the children and play them */
+  disorder_eclient_files(client, choose_playchildren_received,
+                         choose_get_track(iter),
+                         NULL/*re*/,
+                         NULL);
+  /* Expand the node */
+  gtk_tree_view_expand_row(GTK_TREE_VIEW(choose_view), path, FALSE);
+}
+
+static void choose_playchildren_received(void attribute((unused)) *v,
+                                         const char *err,
+                                         int nvec, char **vec) {
+  if(err) {
+    popup_protocol_error(0, err);
+    return;
+  }
+  for(int n = 0; n < nvec; ++n)
+    disorder_eclient_play(client, vec[n], choose_playchildren_played, NULL);
+}
+
+static void choose_playchildren_played(void attribute((unused)) *v,
+                                       const char *err) {
+  if(err) {
+    popup_protocol_error(0, err);
+    return;
+  }
+}
+
 /** @brief Pop-up menu for choose */
 static struct menuitem choose_menuitems[] = {
   {
     "Play track",
+    GTK_STOCK_MEDIA_PLAY,
     choose_play_activate,
     choose_play_sensitive,
     0,
@@ -221,6 +273,7 @@ static struct menuitem choose_menuitems[] = {
   },
   {
     "Track properties",
+    GTK_STOCK_PROPERTIES,
     choose_properties_activate,
     choose_properties_sensitive,
     0,
@@ -228,13 +281,23 @@ static struct menuitem choose_menuitems[] = {
   },
   {
     "Select children",
+    NULL,
     choose_selectchildren_activate,
     choose_selectchildren_sensitive,
     0,
     0
   },
   {
+    "Play children",
+    NULL,
+    choose_playchildren_activate,
+    choose_selectchildren_sensitive,    /* re-use */
+    0,
+    0
+  },
+  {
     "Deselect all tracks",
+    NULL,
     choose_selectnone_activate,
     choose_selectnone_sensitive,
     0,
index 1208f17..acfddfa 100644 (file)
@@ -515,15 +515,15 @@ GtkWidget *choose_search_widget(void) {
   gtk_widget_set_style(choose_search_entry, tool_style);
   g_signal_connect(choose_search_entry, "changed",
                    G_CALLBACK(choose_search_entry_changed), 0);
-  gtk_tooltips_set_tip(tips, choose_search_entry,
-                       "Enter search terms here; search is automatic", "");
+  gtk_widget_set_tooltip_text(choose_search_entry,
+                              "Enter search terms here; search is automatic");
 
   /* Cancel button to clear the search */
   choose_clear = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
   gtk_widget_set_style(choose_clear, tool_style);
   g_signal_connect(G_OBJECT(choose_clear), "clicked",
                    G_CALLBACK(choose_clear_clicked), 0);
-  gtk_tooltips_set_tip(tips, choose_clear, "Clear search terms", "");
+  gtk_widget_set_tooltip_text(choose_clear, "Clear search terms");
 
   /* Up and down buttons to find previous/next results; initially they are not
    * usable as there are no search results. */
index b9a9e7e..a1d50c1 100644 (file)
 
 #include "disobedience.h"
 #include "choose.h"
+#include "multidrag.h"
+#include "queue-generic.h"
 #include <gdk/gdkkeysyms.h>
 
+/** @brief Drag types */
+const GtkTargetEntry choose_targets[] = {
+  {
+    PLAYABLE_TRACKS,                             /* drag type */
+    GTK_TARGET_SAME_APP|GTK_TARGET_OTHER_WIDGET, /* copying between widgets */
+    PLAYABLE_TRACKS_ID                           /* ID value */
+  },
+  {
+    .target = NULL
+  }
+};
+
 /** @brief The current selection tree */
 GtkTreeStore *choose_store;
 
@@ -538,6 +552,60 @@ static gboolean choose_key_event(GtkWidget attribute((unused)) *widget,
   return TRUE;                          /* Handled it */
 }
 
+static gboolean choose_multidrag_predicate(GtkTreePath attribute((unused)) *path,
+                                           GtkTreeIter *iter) {
+  return choose_is_file(iter);
+}
+
+
+/** @brief Callback to add selected tracks to the selection data
+ *
+ * Called from choose_drag_data_get().
+ */
+static void choose_drag_data_get_collect(GtkTreeModel attribute((unused)) *model,
+                                         GtkTreePath attribute((unused)) *path,
+                                         GtkTreeIter *iter,
+                                         gpointer data) {
+  struct dynstr *const result = data;
+
+  if(choose_is_file(iter)) {            /* no diretories */
+    dynstr_append_string(result, "");   /* no ID */
+    dynstr_append(result, '\n');
+    dynstr_append_string(result, choose_get_track(iter));
+    dynstr_append(result, '\n');
+  }
+}
+
+/** @brief Called to extract the dragged data from the choose view
+ * @param w Source widget (the tree view)
+ * @param dc Drag context
+ * @param data Where to put the answer
+ * @param info_ Target @c info parameter
+ * @param time_ Time data requested (for some reason not a @c time_t)
+ * @param user_data The queuelike
+ *
+ * Closely analogous to ql_drag_data_get(), and uses the same data format.
+ * IDs are sent as empty strings.
+ */
+static void choose_drag_data_get(GtkWidget *w,
+                                 GdkDragContext attribute((unused)) *dc,
+                                 GtkSelectionData *data,
+                                 guint attribute((unused)) info_,
+                                 guint attribute((unused)) time_,
+                                 gpointer attribute((unused)) user_data) {
+  struct dynstr result[1];
+  GtkTreeSelection *sel;
+
+  sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));
+  dynstr_init(result);
+  gtk_tree_selection_selected_foreach(sel,
+                                      choose_drag_data_get_collect,
+                                      result);
+  gtk_selection_data_set(data,
+                         GDK_TARGET_STRING,
+                         8, (guchar *)result->vec, result->nvec);
+}
+
 /** @brief Create the choose tab */
 GtkWidget *choose_widget(void) {
   /* Create the tree store. */
@@ -643,6 +711,17 @@ GtkWidget *choose_widget(void) {
   g_signal_connect(choose_view, "key-release-event",
                    G_CALLBACK(choose_key_event), choose_search_entry);
 
+  /* Enable dragging of tracks out */
+  gtk_drag_source_set(choose_view,
+                      GDK_BUTTON1_MASK,
+                      choose_targets,
+                      1,
+                      GDK_ACTION_COPY);
+  g_signal_connect(choose_view, "drag-data-get",
+                   G_CALLBACK(choose_drag_data_get), NULL);
+  make_treeview_multidrag(choose_view,
+                          choose_multidrag_predicate);
+
   return vbox;
 }
 
index dd40597..45c6c0a 100644 (file)
@@ -37,7 +37,7 @@ static gboolean gtkclient_prepare(GSource *source,
                                  gint *timeout) {
   const struct eclient_source *esource = (struct eclient_source *)source;
   D(("gtkclient_prepare"));
-  if(time(0) > esource->last_poll + 10)
+  if(xtime(0) > esource->last_poll + 10)
     return TRUE;               /* timed out */
   *timeout = 3000/*milliseconds*/;
   return FALSE;                        /* please poll */
@@ -65,7 +65,7 @@ static gboolean gtkclient_dispatch(GSource *source,
     mode |= DISORDER_POLL_READ;
   if(revents & (G_IO_OUT|G_IO_HUP|G_IO_ERR))
     mode |= DISORDER_POLL_WRITE;
-  time(&esource->last_poll);
+  xtime(&esource->last_poll);
   disorder_eclient_polled(esource->client, mode);
   return TRUE;                          /* ??? not documented */
 }
index c512059..b2dfce0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2006-2008 Richard Kettlewell
+ * Copyright (C) 2006-2009 Richard Kettlewell
  *
  * 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
  */
 
 #include "disobedience.h"
-#include "mixer.h"
 
 /* Forward declarations ---------------------------------------------------- */
 
 struct icon;
 
-static void clicked_icon(GtkButton *, gpointer);
+static void clicked_icon(GtkToolButton *, gpointer);
+static void toggled_icon(GtkToggleToolButton *button,
+                         gpointer user_data);
 static void clicked_menu(GtkMenuItem *, gpointer userdata);
 static void toggled_menu(GtkCheckMenuItem *, gpointer userdata);
 
@@ -52,12 +53,18 @@ static void icon_changed(const char *event,
 static void volume_changed(const char *event,
                            void *eventdata,
                            void *callbackdata);
+static void control_minimode(const char *event,
+                             void *eventdata,
+                             void *callbackdata);
 
 /* Control bar ------------------------------------------------------------- */
 
 /** @brief Guard against feedback */
 int suppress_actions = 1;
 
+/** @brief Toolbar widget */
+static GtkWidget *toolbar;
+
 /** @brief Definition of an icon
  *
  * We have two kinds of icon:
@@ -70,21 +77,27 @@ int suppress_actions = 1;
  * (All icons can be sensitive or insensitive, separately to the above.)
  */
 struct icon {
-  /** @brief Filename for 'on' image */
-  const char *icon_on;
+  /** @brief TRUE to use GTK+ stock icons instead of filenames */
+  gboolean stock;
+
+  /** @brief TRUE for toggle buttons, FALSE for action buttons */
+  gboolean toggle;
+  
+  /** @brief Filename for image or stock string */
+  const char *icon;
 
   /** @brief Text for 'on' tooltip */
   const char *tip_on;
 
-  /** @brief Filename for 'off' image or NULL for an action icon */
-  const char *icon_off;
-
-  /** @brief Text for 'off tooltip */
+  /** @brief Text for 'off' tooltip */
   const char *tip_off;
 
   /** @brief Associated menu item or NULL */
   const char *menuitem;
 
+  /** @brief Label text */
+  const char *label;
+
   /** @brief Events that change this icon, separated by spaces */
   const char *events;
 
@@ -122,16 +135,16 @@ struct icon {
   /** @brief Pointer to menu item */
   GtkWidget *item;
 
-  GtkWidget *image_on;
-  GtkWidget *image_off;
+  GtkWidget *image;
 };
 
 static int pause_resume_on(void) {
-  return !(last_state & DISORDER_TRACK_PAUSED);
+  return !!(last_state & DISORDER_TRACK_PAUSED);
 }
 
 static int pause_resume_sensitive(void) {
-  return !!(last_state & DISORDER_PLAYING)
+  return playing_track
+    && !!(last_state & DISORDER_PLAYING)
     && (last_rights & RIGHT_PAUSE);
 }
 
@@ -167,19 +180,23 @@ static int rtp_sensitive(void) {
 /** @brief Table of all icons */
 static struct icon icons[] = {
   {
-    icon_on: "pause32.png",
-    tip_on: "Pause playing track",
-    icon_off: "play32.png",
-    tip_off: "Resume playing track",
+    toggle: TRUE,
+    stock: TRUE,
+    icon: GTK_STOCK_MEDIA_PAUSE,
+    label: "Pause",
+    tip_on: "Resume playing track",
+    tip_off: "Pause playing track",
     menuitem: "<GdisorderMain>/Control/Playing",
     on: pause_resume_on,
     sensitive: pause_resume_sensitive,
-    action_go_on: disorder_eclient_resume,
-    action_go_off: disorder_eclient_pause,
-    events: "pause-changed playing-changed rights-changed",
+    action_go_on: disorder_eclient_pause,
+    action_go_off: disorder_eclient_resume,
+    events: "pause-changed playing-changed rights-changed playing-track-changed",
   },
   {
-    icon_on: "cross32.png",
+    stock: TRUE,
+    icon: GTK_STOCK_STOP,
+    label: "Scratch",
     tip_on: "Cancel playing track",
     menuitem: "<GdisorderMain>/Control/Scratch",
     sensitive: scratch_sensitive,
@@ -187,9 +204,11 @@ static struct icon icons[] = {
     events: "playing-track-changed rights-changed",
   },
   {
-    icon_on: "randomenabled32.png",
+    toggle: TRUE,
+    stock: FALSE,
+    icon: "cards24.png",
+    label: "Random",
     tip_on: "Disable random play",
-    icon_off: "randomdisabled32.png",
     tip_off: "Enable random play",
     menuitem: "<GdisorderMain>/Control/Random play",
     on: random_enabled,
@@ -199,9 +218,11 @@ static struct icon icons[] = {
     events: "random-changed rights-changed",
   },
   {
-    icon_on: "playenabled32.png",
+    toggle: TRUE,
+    stock: TRUE,
+    icon: GTK_STOCK_MEDIA_PLAY,
+    label: "Play",
     tip_on: "Disable play",
-    icon_off: "playdisabled32.png",
     tip_off: "Enable play",
     on: playing_enabled,
     sensitive: playing_sensitive,
@@ -210,9 +231,11 @@ static struct icon icons[] = {
     events: "enabled-changed rights-changed",
   },
   {
-    icon_on: "rtpenabled32.png",
+    toggle: TRUE,
+    stock: TRUE,
+    icon: GTK_STOCK_CONNECT,
+    label: "RTP",
     tip_on: "Stop playing network stream",
-    icon_off: "rtpdisabled32.png",
     tip_off: "Play network stream",
     menuitem: "<GdisorderMain>/Control/Network player",
     on: rtp_enabled,
@@ -233,36 +256,52 @@ static GtkWidget *balance_widget;
 
 /** @brief Create the control bar */
 GtkWidget *control_widget(void) {
-  GtkWidget *hbox = gtk_hbox_new(FALSE, 1), *vbox;
+  GtkWidget *hbox = gtk_hbox_new(FALSE, 1);
   int n;
 
   D(("control_widget"));
   assert(mainmenufactory);              /* ordering must be right */
+  toolbar = gtk_toolbar_new();
+  /* Don't permit overflow arrow as otherwise the toolbar isn't greedy enough
+   * in asking for space.  The ideal is probably to make the volume and balance
+   * sliders hang down from the toolbar so it unavoidably gets the whole width
+   * of the window to play with. */
+  gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar), FALSE);
+  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), 
+                        full_mode ? GTK_TOOLBAR_BOTH : GTK_TOOLBAR_ICONS);
   for(n = 0; n < NICONS; ++n) {
-    /* Create the button */
-    icons[n].button = gtk_button_new();
+    struct icon *const icon = &icons[n];
+    icon->button = (icon->toggle
+                    ? GTK_WIDGET(gtk_toggle_tool_button_new())
+                    : GTK_WIDGET(gtk_tool_button_new(NULL, NULL)));
     gtk_widget_set_style(icons[n].button, tool_style);
-    icons[n].image_on = gtk_image_new_from_pixbuf(find_image(icons[n].icon_on));
-    gtk_widget_set_style(icons[n].image_on, tool_style);
-    g_object_ref(icons[n].image_on);
-    /* If it's a toggle icon, create the 'off' half too */
-    if(icons[n].icon_off) {
-      icons[n].image_off = gtk_image_new_from_pixbuf(find_image(icons[n].icon_off));
-      gtk_widget_set_style(icons[n].image_off, tool_style);
-      g_object_ref(icons[n].image_off);
+    if(icons[n].stock) {
+      /* We'll use the stock icons for this one */
+      icon->image = gtk_image_new_from_stock(icons[n].icon,
+                                             GTK_ICON_SIZE_LARGE_TOOLBAR);
+    } else {
+      /* Create the 'on' image */
+      icon->image = gtk_image_new_from_pixbuf(find_image(icons[n].icon));
     }
-    g_signal_connect(G_OBJECT(icons[n].button), "clicked",
-                     G_CALLBACK(clicked_icon), &icons[n]);
-    /* pop the icon in a vbox so it doesn't get vertically stretch if there are
-     * taller things in the control bar */
-    vbox = gtk_vbox_new(FALSE, 0);
-    gtk_box_pack_start(GTK_BOX(vbox), icons[n].button, TRUE, FALSE, 0);
-    gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
+    assert(icon->image);
+    gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(icon->button),
+                                    icon->image);
+    gtk_tool_button_set_label(GTK_TOOL_BUTTON(icon->button),
+                                    icon->label);
+    if(icon->toggle)
+      g_signal_connect(G_OBJECT(icon->button), "toggled",
+                       G_CALLBACK(toggled_icon), icon);
+    else
+      g_signal_connect(G_OBJECT(icon->button), "clicked",
+                       G_CALLBACK(clicked_icon), icon);
+    gtk_toolbar_insert(GTK_TOOLBAR(toolbar),
+                       GTK_TOOL_ITEM(icon->button),
+                       -1);
     if(icons[n].menuitem) {
       /* Find the menu item */
       icons[n].item = gtk_item_factory_get_widget(mainmenufactory,
                                                   icons[n].menuitem);
-      if(icons[n].icon_off)
+      if(icon->toggle)
         g_signal_connect(G_OBJECT(icons[n].item), "toggled",
                          G_CALLBACK(toggled_menu), &icons[n]);
       else
@@ -288,12 +327,16 @@ GtkWidget *control_widget(void) {
   gtk_widget_set_style(balance_widget, tool_style);
   gtk_scale_set_digits(GTK_SCALE(volume_widget), 10);
   gtk_scale_set_digits(GTK_SCALE(balance_widget), 10);
-  gtk_widget_set_size_request(volume_widget, 192, -1);
-  gtk_widget_set_size_request(balance_widget, 192, -1);
-  gtk_tooltips_set_tip(tips, volume_widget, "Volume", "");
-  gtk_tooltips_set_tip(tips, balance_widget, "Balance", "");
-  gtk_box_pack_start(GTK_BOX(hbox), volume_widget, FALSE, TRUE, 0);
-  gtk_box_pack_start(GTK_BOX(hbox), balance_widget, FALSE, TRUE, 0);
+  gtk_widget_set_size_request(volume_widget, 128, -1);
+  gtk_widget_set_size_request(balance_widget, 128, -1);
+  gtk_widget_set_tooltip_text(volume_widget, "Volume");
+  gtk_widget_set_tooltip_text(balance_widget, "Balance");
+  gtk_box_pack_start(GTK_BOX(hbox), toolbar,
+                     FALSE/*expand*/, TRUE/*fill*/, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), volume_widget,
+                     FALSE/*expand*/, TRUE/*fill*/, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), balance_widget,
+                     FALSE/*expand*/, TRUE/*fill*/, 0);
   /* space updates rather than hammering the server */
   gtk_range_set_update_policy(GTK_RANGE(volume_widget), GTK_UPDATE_DELAYED);
   gtk_range_set_update_policy(GTK_RANGE(balance_widget), GTK_UPDATE_DELAYED);
@@ -309,29 +352,31 @@ GtkWidget *control_widget(void) {
                    G_CALLBACK(format_balance), 0);
   event_register("volume-changed", volume_changed, 0);
   event_register("rtp-changed", volume_changed, 0);
+  event_register("mini-mode-changed", control_minimode, 0);
   return hbox;
 }
 
+/** @brief Return TRUE if volume setting is supported */
+static int volume_supported(void) {
+  /* TODO: if the server doesn't know how to set the volume [but isn't using
+   * network play] then we should have volume_supported = FALSE */
+  return (!rtp_supported
+          || (rtp_supported && backend && backend->set_volume));
+}
+
 /** @brief Update the volume control when it changes */
 static void volume_changed(const char attribute((unused)) *event,
                            void attribute((unused)) *eventdata,
                            void attribute((unused)) *callbackdata) {
   double l, r;
-  gboolean volume_supported;
 
   D(("volume_changed"));
   ++suppress_actions;
   /* Only display volume/balance controls if they will work */
-  if(!rtp_supported
-     || (rtp_supported && mixer_supported(DEFAULT_BACKEND)))
-    volume_supported = TRUE;
-  else
-    volume_supported = FALSE;
-  /* TODO: if the server doesn't know how to set the volume [but isn't using
-   * network play] then we should have volume_supported = FALSE */
-  if(volume_supported) {
+  if(volume_supported()) {
     gtk_widget_show(volume_widget);
-    gtk_widget_show(balance_widget);
+    if(full_mode)
+      gtk_widget_show(balance_widget);
     l = volume_l / 100.0;
     r = volume_r / 100.0;
     gtk_adjustment_set_value(volume_adj, volume(l, r) * goesupto);
@@ -353,34 +398,26 @@ static void icon_changed(const char attribute((unused)) *event,
   int on = icon->on ? icon->on() : 1;
   int sensitive = icon->sensitive ? icon->sensitive() : 1;
   //fprintf(stderr, "sensitive->%d\n", sensitive);
-  GtkWidget *child, *newchild;
 
   ++suppress_actions;
   /* If the connection is down nothing is ever usable */
   if(!(last_state & DISORDER_CONNECTED))
     sensitive = 0;
-  //fprintf(stderr, "(checked connected) sensitive->%d\n", sensitive);
-  /* Replace the child */
-  newchild = on ? icon->image_on : icon->image_off;
-  child = gtk_bin_get_child(GTK_BIN(icon->button));
-  if(child != newchild) {
-    if(child)
-      gtk_container_remove(GTK_CONTAINER(icon->button), child);
-    gtk_container_add(GTK_CONTAINER(icon->button), newchild);
-    gtk_widget_show(newchild);
-  }
+  if(icon->toggle)
+    gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(icon->button),
+                                      on);
   /* If you disable play or random play NOT via the icon (for instance, via the
    * edit menu or via a completely separate command line invocation) then the
    * icon shows up as insensitive.  Hover the mouse over it and the correct
    * state is immediately displayed.  sensitive and GTK_WIDGET_SENSITIVE show
    * it to be in the correct state, so I think this is may be a GTK+ bug. */
   if(icon->tip_on)
-    gtk_tooltips_set_tip(tips, icon->button,
-                           on ? icon->tip_on : icon->tip_off, "");
+    gtk_widget_set_tooltip_text(icon->button,
+                                on ? icon->tip_on : icon->tip_off);
   gtk_widget_set_sensitive(icon->button, sensitive);
   /* Icons with an associated menu item */
   if(icon->item) {
-    if(icon->icon_off)
+    if(icon->toggle)
       gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(icon->item), on);
     gtk_widget_set_sensitive(icon->item, sensitive);
   }
@@ -393,13 +430,22 @@ static void icon_action_completed(void attribute((unused)) *v,
     popup_protocol_error(0, err);
 }
 
-static void clicked_icon(GtkButton attribute((unused)) *button,
+static void clicked_icon(GtkToolButton attribute((unused)) *button,
                          gpointer userdata) {
   const struct icon *icon = userdata;
 
   if(suppress_actions)
     return;
-  if(!icon->on || icon->on())
+  icon->action_go_off(client, icon_action_completed, 0);
+}
+
+static void toggled_icon(GtkToggleToolButton attribute((unused)) *button,
+                         gpointer user_data) {
+  const struct icon *icon = user_data;
+
+  if(suppress_actions)
+    return;
+  if(icon->on())
     icon->action_go_off(client, icon_action_completed, 0);
   else
     icon->action_go_on(client, icon_action_completed, 0);
@@ -412,7 +458,7 @@ static void clicked_menu(GtkMenuItem attribute((unused)) *menuitem,
 
 static void toggled_menu(GtkCheckMenuItem attribute((unused)) *menuitem,
                          gpointer userdata) {
-  clicked_icon(NULL, userdata);
+  toggled_icon(NULL, userdata);
 }
 
 /** @brief Called when a volume command completes */
@@ -444,7 +490,8 @@ static void volume_adjusted(GtkAdjustment attribute((unused)) *a,
    * from the log. */
   if(rtp_supported) {
     int l = nearbyint(left(v, b) * 100), r = nearbyint(right(v, b) * 100);
-    mixer_control(DEFAULT_BACKEND, &l, &r, 1);
+    if(backend && backend->set_volume)
+      backend->set_volume(&l, &r);
   } else
     disorder_eclient_volume(client, volume_completed,
                             nearbyint(left(v, b) * 100),
@@ -559,6 +606,20 @@ static int disable_rtp(disorder_eclient attribute((unused)) *c,
   return 0;
 }
 
+static void control_minimode(const char attribute((unused)) *event,
+                             void attribute((unused)) *evendata,
+                             void attribute((unused)) *callbackdata) {
+  if(full_mode && volume_supported()) {
+    gtk_widget_show(balance_widget);
+    gtk_scale_set_value_pos(GTK_SCALE(volume_widget), GTK_POS_TOP);
+  } else {
+    gtk_widget_hide(balance_widget);
+    gtk_scale_set_value_pos(GTK_SCALE(volume_widget), GTK_POS_RIGHT);
+  }
+  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), 
+                        full_mode ? GTK_TOOLBAR_BOTH : GTK_TOOLBAR_ICONS);
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index ab51bb3..639eb73 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2006, 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2006-2009 Richard Kettlewell
  *
  * 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
  */
 
 #include "disobedience.h"
-#include "mixer.h"
 #include "version.h"
 
 #include <getopt.h>
 #include <locale.h>
 #include <pcre.h>
+#include <gcrypt.h>
 
 /* Apologies for the numerous de-consting casts, but GLib et al do not seem to
  * have heard of const. */
@@ -44,6 +44,9 @@ GtkWidget *report_label;
 /** @brief Main tab group */
 GtkWidget *tabs;
 
+/** @brief Mini-mode widget for playing track */
+GtkWidget *playing_mini;
+
 /** @brief Main client */
 disorder_eclient *client;
 
@@ -68,6 +71,9 @@ int volume_l;
 /** @brief Right channel volume */
 int volume_r;
 
+/** @brief Audio backend */
+const struct uaudio *backend;
+
 double goesupto = 10;                   /* volume upper bound */
 
 /** @brief True if a NOP is in flight */
@@ -82,9 +88,6 @@ static int rights_lookup_in_flight;
 /** @brief Current rights bitmap */
 rights_type last_rights;
 
-/** @brief Global tooltip group */
-GtkTooltips *tips;
-
 /** @brief True if RTP play is available
  *
  * This is a bit of a bodge...
@@ -100,6 +103,10 @@ const char *server_version;
 /** @brief Parsed server version */
 long server_version_bytes;
 
+static GtkWidget *queue;
+
+static GtkWidget *notebook_box;
+
 static void check_rtp_address(const char *event,
                               void *eventdata,
                               void *callbackdata);
@@ -154,7 +161,7 @@ static GtkWidget *notebook(void) {
    * produces not too dreadful appearance */
   gtk_widget_set_style(tabs, tool_style);
   g_signal_connect(tabs, "switch-page", G_CALLBACK(tab_switched), 0);
-  gtk_notebook_append_page(GTK_NOTEBOOK(tabs), queue_widget(),
+  gtk_notebook_append_page(GTK_NOTEBOOK(tabs), queue = queue_widget(),
                            gtk_label_new("Queue"));
   gtk_notebook_append_page(GTK_NOTEBOOK(tabs), recent_widget(),
                            gtk_label_new("Recent"));
@@ -165,18 +172,78 @@ static GtkWidget *notebook(void) {
   return tabs;
 }
 
+/* Tracking of window sizes */
+static int toplevel_width = 640, toplevel_height = 480;
+static int mini_width = 480, mini_height = 140;
+static struct timeval last_mode_switch;
+
+static void main_minimode(const char attribute((unused)) *event,
+                          void attribute((unused)) *evendata,
+                          void attribute((unused)) *callbackdata) {
+  if(full_mode) {
+    gtk_window_resize(GTK_WINDOW(toplevel), toplevel_width, toplevel_height);
+    gtk_widget_show(tabs);
+    gtk_widget_hide(playing_mini);
+    /* Show the queue (bit confusing otherwise!) */
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(tabs), 0);
+  } else {
+    gtk_window_resize(GTK_WINDOW(toplevel), mini_width, mini_height);
+    gtk_widget_hide(tabs);
+    gtk_widget_show(playing_mini);
+  }
+  xgettimeofday(&last_mode_switch, NULL);
+}
+
+/* Called when the window size is allocate */
+static void toplevel_size_allocate(GtkWidget attribute((unused)) *w,
+                                   GtkAllocation *a,
+                                   gpointer attribute((unused)) user_data) {
+  struct timeval now;
+  xgettimeofday(&now, NULL);
+  if(tvdouble(tvsub(now, last_mode_switch)) < 0.5) {
+    /* Suppress size-allocate signals that are within half a second of a mode
+     * switch: they are quite likely to be the result of re-arranging widgets
+     * within the old size, not the application of the new size.  Yes, this is
+     * a disgusting hack! */
+    return;                             /* OMG too soon! */
+  }
+  if(full_mode) {
+    toplevel_width = a->width;
+    toplevel_height = a->height;
+  } else {
+    mini_width = a->width;
+    mini_height = a->height;
+  }
+}
+
+/* Periodically check the toplevel's size
+ * (the hack in toplevel_size_allocate() means we could in principle
+ * miss a user-initiated resize)
+ */
+static void check_toplevel_size(const char attribute((unused)) *event,
+                                void attribute((unused)) *evendata,
+                                void attribute((unused)) *callbackdata) {
+  GtkAllocation a;
+  gtk_window_get_size(GTK_WINDOW(toplevel), &a.width, &a.height);
+  toplevel_size_allocate(NULL, &a, NULL);
+}
+
 /** @brief Create and populate the main window */
 static void make_toplevel_window(void) {
-  GtkWidget *const vbox = gtk_vbox_new(FALSE, 1);
+  GtkWidget *const vbox = gtk_vbox_new(FALSE/*homogeneous*/, 1/*spacing*/);
   GtkWidget *const rb = report_box();
 
   D(("top_window"));
   toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
   /* default size is too small */
-  gtk_window_set_default_size(GTK_WINDOW(toplevel), 640, 480);
+  gtk_window_set_default_size(GTK_WINDOW(toplevel),
+                              toplevel_width, toplevel_height);
   /* terminate on close */
   g_signal_connect(G_OBJECT(toplevel), "delete_event",
                    G_CALLBACK(delete_event), NULL);
+  /* track size */
+  g_signal_connect(G_OBJECT(toplevel), "size-allocate",
+                   G_CALLBACK(toplevel_size_allocate), NULL);
   /* lay out the window */
   gtk_window_set_title(GTK_WINDOW(toplevel), "Disobedience");
   gtk_container_add(GTK_CONTAINER(toplevel), vbox);
@@ -191,13 +258,23 @@ static void make_toplevel_window(void) {
                      FALSE,             /* expand */
                      FALSE,             /* fill */
                      0);
-  gtk_container_add(GTK_CONTAINER(vbox), notebook());
+  playing_mini = playing_widget();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                     playing_mini,
+                     FALSE,
+                     FALSE,
+                     0);
+  notebook_box = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(notebook_box), notebook());
+  gtk_container_add(GTK_CONTAINER(vbox), notebook_box);
   gtk_box_pack_end(GTK_BOX(vbox),
                    rb,
                    FALSE,             /* expand */
                    FALSE,             /* fill */
                    0);
   gtk_widget_set_style(toplevel, tool_style);
+  event_register("mini-mode-changed", main_minimode, 0);
+  event_register("periodic-fast", check_toplevel_size, 0);
 }
 
 static void userinfo_rights_completed(void attribute((unused)) *v,
@@ -240,6 +317,7 @@ static gboolean periodic_slow(gpointer attribute((unused)) data) {
   /* Update everything to be sure that the connection to the server hasn't
    * mysteriously gone stale on us. */
   all_update();
+  event_raise("periodic-slow", 0);
   /* Recheck RTP status too */
   check_rtp_address(0, 0, 0);
   return TRUE;                          /* don't remove me */
@@ -263,10 +341,10 @@ static gboolean periodic_fast(gpointer attribute((unused)) data) {
   }
   last = now;
 #endif
-  if(rtp_supported && mixer_supported(DEFAULT_BACKEND)) {
+  if(rtp_supported && backend && backend->get_volume) {
     int nl, nr;
-    if(!mixer_control(DEFAULT_BACKEND, &nl, &nr, 0)
-       && (nl != volume_l || nr != volume_r)) {
+    backend->get_volume(&nl, &nr);
+    if(nl != volume_l || nr != volume_r) {
       volume_l = nl;
       volume_r = nr;
       event_raise("volume-changed", 0);
@@ -282,6 +360,7 @@ static gboolean periodic_fast(gpointer attribute((unused)) data) {
     recheck_rights = 0;
   if(recheck_rights)
     check_rights();
+  event_raise("periodic-fast", 0);
   return TRUE;
 }
 
@@ -432,7 +511,7 @@ int main(int argc, char **argv) {
   /* garbage-collect PCRE's memory */
   pcre_malloc = xmalloc;
   pcre_free = xfree;
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
   gtkok = gtk_init_check(&argc, &argv);
   while((n = getopt_long(argc, argv, "hVc:dtHC", options, 0)) >= 0) {
     switch(n) {
@@ -441,18 +520,29 @@ int main(int argc, char **argv) {
     case 'c': configfile = optarg; break;
     case 'd': debugging = 1; break;
     case 't': goesupto = 11; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   if(!gtkok)
-    fatal(0, "failed to initialize GTK+");
+    disorder_fatal(0, "failed to initialize GTK+");
+  /* gcrypt initialization */
+  if(!gcry_check_version(NULL))
+    disorder_fatal(0, "gcry_check_version failed");
+  gcry_control(GCRYCTL_INIT_SECMEM, 0);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
   signal(SIGPIPE, SIG_IGN);
   init_styles();
   load_settings();
   /* create the event loop */
   D(("create main loop"));
   mainloop = g_main_loop_new(0, 0);
-  if(config_read(0)) fatal(0, "cannot read configuration");
+  if(config_read(0, NULL)) disorder_fatal(0, "cannot read configuration");
+  /* we'll need mixer support */
+  backend = uaudio_apis[0];
+  if(backend->configure)
+    backend->configure();
+  if(backend->open_mixer)
+    backend->open_mixer();
   /* create the clients */
   if(!(client = gtkclient())
      || !(logclient = gtkclient()))
@@ -460,12 +550,11 @@ int main(int argc, char **argv) {
   /* periodic operations (e.g. expiring the cache, checking local volume) */
   g_timeout_add(600000/*milliseconds*/, periodic_slow, 0);
   g_timeout_add(1000/*milliseconds*/, periodic_fast, 0);
-  /* global tooltips */
-  tips = gtk_tooltips_new();
   make_toplevel_window();
   /* reset styles now everything has its name */
   gtk_rc_reset_styles(gtk_settings_get_for_screen(gdk_screen_get_default()));
   gtk_widget_show_all(toplevel);
+  gtk_widget_hide(playing_mini);
   /* issue a NOP every so often */
   g_timeout_add_full(G_PRIORITY_LOW,
                      2000/*interval, ms*/,
@@ -479,6 +568,7 @@ int main(int argc, char **argv) {
   disorder_eclient_version(client, version_completed, 0);
   event_register("log-connected", check_rtp_address, 0);
   suppress_actions = 0;
+  playlists_init();
   /* If no password is set yet pop up a login box */
   if(!config->password)
     login_box();
index db4bff0..719eea4 100644 (file)
@@ -47,6 +47,7 @@
 #include "eventdist.h"
 #include "split.h"
 #include "timeval.h"
+#include "uaudio.h"
 
 #include <glib.h>
 #include <gtk/gtk.h>
@@ -84,6 +85,11 @@ struct button {
   void (*clicked)(GtkButton *button, gpointer userdata);
   const char *tip;
   GtkWidget *widget;
+  void (*pack)(GtkBox *box,
+               GtkWidget *child,
+               gboolean expand,
+               gboolean fill,
+               guint padding);
 };
 
 /* Variables --------------------------------------------------------------- */
@@ -100,10 +106,10 @@ extern int playing;                     /* true if playing some track */
 extern int volume_l, volume_r;          /* current volume */
 extern double goesupto;                 /* volume upper bound */
 extern int choosealpha;                 /* break up choose by letter */
-extern GtkTooltips *tips;
 extern int rtp_supported;
 extern int rtp_is_running;
 extern GtkItemFactory *mainmenufactory;
+extern const struct uaudio *backend;
 
 extern const disorder_eclient_log_callbacks log_callbacks;
 
@@ -116,7 +122,8 @@ void popup_protocol_error(int code,
                           const char *msg);
 /* Report an error */
 
-void properties(int ntracks, const char **tracks);
+void properties(int ntracks, const char **tracks,
+                GtkWidget *parent);
 /* Pop up a properties window for a list of tracks */
 
 GtkWidget *scroll_widget(GtkWidget *child);
@@ -133,7 +140,8 @@ void popup_submsg(GtkWidget *parent, GtkMessageType mt, const char *msg);
 
 void fpopup_msg(GtkMessageType mt, const char *fmt, ...);
 
-struct progress_window *progress_window_new(const char *title);
+struct progress_window *progress_window_new(const char *title,
+                                            GtkWidget *parent);
 /* Pop up a progress window */
 
 void progress_window_progress(struct progress_window *pw,
@@ -158,6 +166,7 @@ void all_update(void);
 
 GtkWidget *menubar(GtkWidget *w);
 /* Create the menu bar */
+int full_mode;
 
 void users_set_sensitive(int sensitive);
 
@@ -171,6 +180,7 @@ extern int suppress_actions;
 /* Queue/Recent/Added */
 
 GtkWidget *queue_widget(void);
+GtkWidget *playing_widget(void);
 GtkWidget *recent_widget(void);
 GtkWidget *added_widget(void);
 /* Create widgets for displaying the queue, the recently played list and the
@@ -211,6 +221,8 @@ void choose_update(void);
 void play_completed(void *v,
                     const char *err);
 
+extern const GtkTargetEntry choose_targets[];
+
 /* Login details */
 
 void login_box(void);
@@ -223,7 +235,7 @@ void manage_users(void);
 
 /* Help */
 
-void popup_help(void);
+void popup_help(const char *what);
 
 /* RTP */
 
@@ -250,6 +262,18 @@ void load_settings(void);
 void set_tool_colors(GtkWidget *w);
 void popup_settings(void);
 
+/* Playlists */
+
+void playlists_init(void);
+void playlist_window_create(gpointer callback_data,
+                            guint callback_action,
+                            GtkWidget  *menu_item);
+extern char **playlists;
+extern int nplaylists;
+extern GtkWidget *menu_playlists_widget;
+extern GtkWidget *playlists_menu;
+extern GtkWidget *menu_editplaylists_widget;
+
 #endif /* DISOBEDIENCE_H */
 
 /*
index 9408654..ca5a8d8 100644 (file)
 #include <unistd.h>
 
 /** @brief Display the manual page */
-void popup_help(void) {
+void popup_help(const char *what) {
   char *path;
   pid_t pid;
   int w;
 
-  byte_xasprintf(&path, "%s/disobedience.html", pkgdatadir);
+  if(!what)
+    what = "index.html";
+#if __APPLE__
+  if(!strcmp(browser, "open"))
+    /* Apple's open(1) isn't really a web browser so needs some extra hints
+     * that it should see the argument as a URL.  Otherwise it doesn't treat #
+     * specially.  A better answer would be to identify the system web browser
+     * and invoke it directly. */
+    byte_xasprintf(&path, "file:///%s/%s", dochtmldir, what);
+  else
+#endif
+    byte_xasprintf(&path, "%s/%s", dochtmldir, what);
   if(!(pid = xfork())) {
     exitfn = _exit;
     if(!xfork()) {
       execlp(browser, browser, path, (char *)0);
-      fatal(errno, "error executing %s", browser);
+      disorder_fatal(errno, "error executing %s", browser);
     }
     _exit(0);
   }
index 652c4e9..71d2af0 100644 (file)
@@ -42,6 +42,12 @@ static void log_volume(void *v, int l, int r);
 static void log_rescanned(void *v);
 static void log_rights_changed(void *v, rights_type r);
 static void log_adopted(void *v, const char *id, const char *user);
+static void log_playlist_created(void *v,
+                                 const char *playlist, const char *sharing);
+static void log_playlist_modified(void *v,
+                                  const char *playlist, const char *sharing);
+static void log_playlist_deleted(void *v,
+                                 const char *playlist);
 
 /** @brief Callbacks for server state monitoring */
 const disorder_eclient_log_callbacks log_callbacks = {
@@ -59,7 +65,10 @@ const disorder_eclient_log_callbacks log_callbacks = {
   .volume = log_volume,
   .rescanned = log_rescanned,
   .rights_changed = log_rights_changed,
-  .adopted = log_adopted
+  .adopted = log_adopted,
+  .playlist_created = log_playlist_created,
+  .playlist_modified = log_playlist_modified,
+  .playlist_deleted = log_playlist_deleted,
 };
 
 /** @brief Update everything */
@@ -106,6 +115,7 @@ static void log_moved(void attribute((unused)) *v,
 static void log_playing(void attribute((unused)) *v,
                         const char attribute((unused)) *track,
                         const char attribute((unused)) *user) {
+  event_raise("playing-started", 0);
 }
 
 /** @brief Called when a track is added to the queue */
@@ -211,6 +221,23 @@ static void log_adopted(void attribute((unused)) *v,
   event_raise("queue-changed", 0);
 }
 
+static void log_playlist_created(void attribute((unused)) *v,
+                                 const char *playlist,
+                                 const char attribute((unused)) *sharing) {
+  event_raise("playlist-created", (void *)playlist);
+}
+
+static void log_playlist_modified(void attribute((unused)) *v,
+                                  const char *playlist,
+                                  const char attribute((unused)) *sharing) {
+  event_raise("playlist-modified", (void *)playlist);
+}
+
+static void log_playlist_deleted(void attribute((unused)) *v,
+                                 const char *playlist) {
+  event_raise("playlist-deleted", (void *)playlist);
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index 1c6df79..a02c1c6 100644 (file)
@@ -73,7 +73,7 @@ static void default_connect(void) {
   if(config->password)
     return;
   /* If we already have a host and/or port that's good too */
-  if(config->connect.n)
+  if(config->connect.af != -1)
     return;
   /* If there's a suitable socket that's probably what we wanted */
   const char *s = config_get_file("socket");
@@ -84,11 +84,21 @@ static void default_connect(void) {
 }
 
 static const char *get_hostname(void) {
-  return config->connect.n >= 2 ? config->connect.s[0] : "";
+  if(config->connect.af == -1 || !config->connect.address)
+    return "";
+  else
+    return config->connect.address;
 }
 
 static const char *get_service(void) {
-  return config->connect.n >= 2 ? config->connect.s[1] : "";
+  if(config->connect.af == -1)
+    return "";
+  else {
+    char *s;
+
+    byte_xasprintf(&s, "%d", config->connect.port);
+    return s;
+  }
 }
 
 static const char *get_username(void) {
@@ -100,11 +110,13 @@ static const char *get_password(void) {
 }
 
 static void set_hostname(struct config *c, const char *s) {
-  c->connect.s[0] = (char *)s;
+  if(c->connect.af == -1)
+    c->connect.af = AF_UNSPEC;
+  c->connect.address = xstrdup(s);
 }
 
 static void set_service(struct config *c, const char *s) {
-  c->connect.s[1] = (char *)s;
+  c->connect.port = atoi(s);
 }
 
 static void set_username(struct config *c, const char *s) {
@@ -131,13 +143,10 @@ static void login_update_config(struct config *c) {
   size_t n;
   const gboolean remote = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lwi_remote));
 
-  if(remote) {
-    c->connect.n = 2;
-    c->connect.s = xcalloc(2, sizeof (char *));
-  } else {
-    c->connect.n = 0;
-    c->connect.s = 0;
-  }
+  if(remote)
+    c->connect.af = AF_UNSPEC;
+  else
+    c->connect.af = -1;
   for(n = 0; n < NLWIS; ++n)
     if(remote || !(lwis[n].flags & LWI_REMOTE))
       lwis[n].set(c, xstrdup(gtk_entry_get_text(GTK_ENTRY(lwi_entry[n]))));
@@ -161,10 +170,12 @@ static void login_save_config(void) {
                    "password %s\n",
                    quoteutf8(config->username),
                    quoteutf8(config->password));
-  if(rc >= 0 && config->connect.n)
-    rc = fprintf(fp, "connect %s %s\n",
-                 quoteutf8(config->connect.s[0]),
-                 quoteutf8(config->connect.s[1]));
+  if(rc >= 0 && config->connect.af != -1) {
+    char **vec;
+
+    netaddress_format(&config->connect, NULL, &vec);
+    rc = fprintf(fp, "connect %s %s %s\n", vec[0], vec[1], vec[2]);
+  }
   if(rc < 0) {
     fpopup_msg(GTK_MESSAGE_ERROR, "error writing to %s: %s",
                tmp, strerror(errno));
@@ -219,6 +230,12 @@ static void login_cancel(GtkButton attribute((unused)) *button,
   gtk_widget_destroy(login_window);
 }
 
+/** @brief User pressed cancel in the login window */
+static void login_help(GtkButton attribute((unused)) *button,
+                       gpointer attribute((unused)) userdata) {
+  popup_help("intro.html#login");
+}
+
 /** @brief Keypress handler */
 static gboolean login_keypress(GtkWidget attribute((unused)) *widget,
                                GdkEventKey *event,
@@ -240,16 +257,25 @@ static gboolean login_keypress(GtkWidget attribute((unused)) *widget,
 /* Buttons that appear at the bottom of the window */
 static struct button buttons[] = {
   {
-    "Login",
-    login_ok,
-    "(Re-)connect using these settings",
-    0
+    GTK_STOCK_HELP,
+    login_help,
+    "Go to manual",
+    0,
+    gtk_box_pack_start,
   },
   {
     GTK_STOCK_CLOSE,
     login_cancel,
     "Discard changes and close window",
-    0
+    0,
+    gtk_box_pack_end,
+  },
+  {
+    "Login",
+    login_ok,
+    "(Re-)connect using these settings",
+    0,
+    gtk_box_pack_end,
   },
 };
 
@@ -327,7 +353,7 @@ void login_box(void) {
   }
   /* Initial settings */
   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lwi_remote),
-                               config->connect.n >= 2);
+                               config->connect.af != -1);
   lwi_remote_toggled(GTK_TOGGLE_BUTTON(lwi_remote), 0);
   buttonbox = create_buttons(buttons, NBUTTONS);
   vbox = gtk_vbox_new(FALSE, 1);
diff --git a/disobedience/manual/Makefile.am b/disobedience/manual/Makefile.am
new file mode 100644 (file)
index 0000000..cd607f0
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# This file is part of DisOrder.
+# Copyright (C) 2009 Richard Kettlewell
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+dist_dochtml_DATA=index.html intro.html misc.html playlists.html       \
+       properties.html tabs.html window.html disobedience.css          \
+       arch-simple.png button-pause.png button-playing.png             \
+       button-random.png button-rtp.png button-scratch.png             \
+       choose-search.png choose.png disobedience-debian-menu.png       \
+       disobedience-terminal.png disorder-email-confirm.png            \
+       disorder-web-login.png login.png menu-control.png               \
+       menu-edit.png menu-help.png menu-server.png                     \
+       playlist-create.png playlist-picker-menu.png                    \
+       playlist-popup-menu.png playlist-window.png queue-menu.png      \
+       queue.png queue2.png recent.png track-properties.png            \
+       volume-slider.png
diff --git a/disobedience/manual/arch-simple.png b/disobedience/manual/arch-simple.png
new file mode 100644 (file)
index 0000000..c7d965a
Binary files /dev/null and b/disobedience/manual/arch-simple.png differ
diff --git a/disobedience/manual/arch-simple.svg b/disobedience/manual/arch-simple.svg
new file mode 100644 (file)
index 0000000..a03d78a
--- /dev/null
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="400"
+   height="400"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="arch-simple.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/richard/src/disorder.dmanual/disobedience/manual/arch-simple.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="151.50215"
+     inkscape:cy="200"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:snap-global="true"
+     inkscape:window-width="1104"
+     inkscape:window-height="672"
+     inkscape:window-x="321"
+     inkscape:window-y="297">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2383"
+       visible="true"
+       enabled="true"
+       spacingx="8px"
+       spacingy="8px"
+       empspacing="4"
+       color="#007fff"
+       opacity="0.1254902" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <rect
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.96492909999999998;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3224"
+       width="335.9653"
+       height="304.03506"
+       x="7.9824643"
+       y="7.9824643" />
+    <g
+       id="g3161"
+       transform="translate(-16.229471,-255.77051)">
+      <rect
+         y="272.22946"
+         x="32.229473"
+         height="63.541054"
+         width="128.54106"
+         id="rect2385"
+         style="fill:#00ffff;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <text
+         id="text3157"
+         y="312"
+         x="56"
+         style="font-size:24px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="312"
+           x="56"
+           id="tspan3159"
+           sodipodi:role="line">Server</tspan></text>
+    </g>
+    <rect
+       style="fill:#00ffff;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3166"
+       width="160"
+       height="64"
+       x="176.54106"
+       y="16" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="187.13188"
+       y="55.456055"
+       id="text3168"><tspan
+         sodipodi:role="line"
+         id="tspan3170"
+         x="187.13188"
+         y="55.456055">Web interface</tspan></text>
+    <rect
+       style="fill:#00ffff;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3174"
+       width="128.54106"
+       height="63.541054"
+       x="15.053802"
+       y="144.95049" />
+    <text
+       xml:space="preserve"
+       style="font-size:8px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="45.493561"
+       y="208.58369"
+       id="text3176"><tspan
+         sodipodi:role="line"
+         id="tspan3178"
+         x="45.493561"
+         y="208.58369" /></text>
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="25.344841"
+       y="182.23859"
+       id="text3180"><tspan
+         sodipodi:role="line"
+         id="tspan3182"
+         x="25.344841"
+         y="182.23859">RTP player</tspan></text>
+    <rect
+       style="fill:#00ff00;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3184"
+       width="128.54106"
+       height="63.541054"
+       x="16"
+       y="240.45895" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="23.89846"
+       y="279.68555"
+       id="text3186"><tspan
+         sodipodi:role="line"
+         id="tspan3188"
+         x="23.89846"
+         y="279.68555">Sound card</tspan></text>
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 46.824329,80.72103 L 46.824329,112.72103"
+       id="path3190" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 16.980579,112.72103 L 328.98058,112.72103"
+       id="path3192" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 80.541067,112 L 80.541067,144"
+       id="path3194" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="62.652657"
+       y="104.48068"
+       id="text3196"><tspan
+         sodipodi:role="line"
+         id="tspan3198"
+         x="62.652657"
+         y="104.48068">Local Network</tspan></text>
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 80.541067,208 L 80.541067,240"
+       id="path3207" />
+    <rect
+       style="font-size:20px;fill:#00ffff;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3209"
+       width="160.45293"
+       height="63.147457"
+       x="176.08813"
+       y="144.85254" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="188.28726"
+       y="183.88232"
+       id="text3211"><tspan
+         sodipodi:role="line"
+         id="tspan3213"
+         x="188.28726"
+         y="183.88232">Disobedience</tspan></text>
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3.13968921;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 240.54107,112 L 240.54107,143.96567"
+       id="path3218" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 144.54107,48 L 176.54107,48"
+       id="path3220" />
+  </g>
+</svg>
diff --git a/disobedience/manual/button-pause.png b/disobedience/manual/button-pause.png
new file mode 100644 (file)
index 0000000..565f227
Binary files /dev/null and b/disobedience/manual/button-pause.png differ
diff --git a/disobedience/manual/button-playing.png b/disobedience/manual/button-playing.png
new file mode 100644 (file)
index 0000000..7b6fb2a
Binary files /dev/null and b/disobedience/manual/button-playing.png differ
diff --git a/disobedience/manual/button-random.png b/disobedience/manual/button-random.png
new file mode 100644 (file)
index 0000000..9791c2c
Binary files /dev/null and b/disobedience/manual/button-random.png differ
diff --git a/disobedience/manual/button-rtp.png b/disobedience/manual/button-rtp.png
new file mode 100644 (file)
index 0000000..ad0d749
Binary files /dev/null and b/disobedience/manual/button-rtp.png differ
diff --git a/disobedience/manual/button-scratch.png b/disobedience/manual/button-scratch.png
new file mode 100644 (file)
index 0000000..c90bfc5
Binary files /dev/null and b/disobedience/manual/button-scratch.png differ
diff --git a/disobedience/manual/choose-search.png b/disobedience/manual/choose-search.png
new file mode 100644 (file)
index 0000000..bc28194
Binary files /dev/null and b/disobedience/manual/choose-search.png differ
diff --git a/disobedience/manual/choose.png b/disobedience/manual/choose.png
new file mode 100644 (file)
index 0000000..2f88ece
Binary files /dev/null and b/disobedience/manual/choose.png differ
diff --git a/disobedience/manual/disobedience-debian-menu.png b/disobedience/manual/disobedience-debian-menu.png
new file mode 100644 (file)
index 0000000..49bbdb4
Binary files /dev/null and b/disobedience/manual/disobedience-debian-menu.png differ
diff --git a/disobedience/manual/disobedience-terminal.png b/disobedience/manual/disobedience-terminal.png
new file mode 100644 (file)
index 0000000..5cf2e8d
Binary files /dev/null and b/disobedience/manual/disobedience-terminal.png differ
diff --git a/disobedience/manual/disobedience.css b/disobedience/manual/disobedience.css
new file mode 100644 (file)
index 0000000..04b6f80
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+This file is part of DisOrder.
+Copyright (C) 2009 Richard Kettlewell
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* default font and colors */
+body {
+  color: black;
+  background-color: white;
+  font-family: times,serif;
+  font-weight: normal;
+  font-size: 12pt;
+  font-variant: normal
+}
+
+/* general link colors */
+a {
+  color: blue;
+  text-decoration: none
+}
+
+h2 a {
+  color: black
+}
+
+p.chapter a {
+  color: black;
+  font-family: helvetica,sans-serif;
+  font-weight: bold;
+  font-size: 18pt
+}
+
+a:active {
+  color: red
+}
+
+a:visited {
+  color: red
+}
+
+/* title bars */
+h1 {
+  font-family: helvetica,sans-serif;
+  font-weight: bold;
+  font-size: 18pt;
+  font-variant: normal;
+  text-align: center;
+  border: 1px solid black;
+  padding: 0.2em;
+  background-color: #e0e0e0;
+  display: block
+}
+
+/* secondary titles */
+h2 {
+  font-family: helvetica,sans-serif;
+  font-weight: bold;
+  font-size: 16pt;
+  font-variant: normal;
+  display: block
+}
+
+td {
+  vertical-align: top;
+  padding: 8px
+}
+
+td:first-child {
+  text-align: right
+}
+
+table {
+  margin-left: 2em
+}
+
+p.image {
+  text-align: center
+}
diff --git a/disobedience/manual/disorder-email-confirm.png b/disobedience/manual/disorder-email-confirm.png
new file mode 100644 (file)
index 0000000..c535a67
Binary files /dev/null and b/disobedience/manual/disorder-email-confirm.png differ
diff --git a/disobedience/manual/disorder-web-login.png b/disobedience/manual/disorder-web-login.png
new file mode 100644 (file)
index 0000000..3155a4c
Binary files /dev/null and b/disobedience/manual/disorder-web-login.png differ
diff --git a/disobedience/manual/index.html b/disobedience/manual/index.html
new file mode 100644 (file)
index 0000000..7245024
--- /dev/null
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<!--
+This file is part of DisOrder.
+Copyright (C) 2009 Richard Kettlewell
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<html>
+ <head>
+   <title>Disobedience</title>
+   <link rel=stylesheet
+         type="text/css"
+         href="disobedience.css">
+ </head>
+ <body>
+   <h1>Disobedience</h1>
+
+   <p>This is the manual for Disobedience, the graphical client
+   for <a href="http://www.greenend.org.uk/rjk/disorder/">DisOrder</a>.</p>
+
+   <p class=chapter><a href="intro.html">1. Introduction</a></p>
+   <ul>
+     <li>What DisOrder and Disobedience are, and how to get them</li>
+     <li>How to get a DisOrder login</li>
+     <li>How to start Disobedience</li>
+   </ul>
+
+   <p class=chapter><a href="window.html">2. Window Layout</a></p>
+
+   <ul>
+     <li>A tour of the Disobedience window</li>
+   </ul>
+
+   <p class=chapter><a href="tabs.html">3. Tabs</a></p>
+
+   <ul>
+     <li>Detailed descriptions of
+       the <b>Queue</b>, <b>Recent</b>, <b>Choose</b> and <b>Added</b>
+       tabs</li>
+   </ul>
+
+   <p class=chapter><a href="properties.html">4. Track Properties</a></p>
+
+   <ul>
+     <li>How to edit track properties</li>
+   </ul>
+
+   <p class=chapter><a href="playlists.html">5. Playlists</a></p>
+
+   <ul>
+     <li>What playlists are</li>
+     <li>Editing playlists</li>
+   </ul>
+
+   <p class=chapter><a href="misc.html">Appendix</a></p>
+
+   <ul>
+     <li>Network play</li>
+     <li>Reporting bugs</li>
+     <li>Copyright notice</li>
+   </ul>
+
+ </body>
+</html>
diff --git a/disobedience/manual/intro.html b/disobedience/manual/intro.html
new file mode 100644 (file)
index 0000000..3fb0451
--- /dev/null
@@ -0,0 +1,201 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<!--
+This file is part of DisOrder.
+Copyright (C) 2009 Richard Kettlewell
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<html>
+ <head>
+   <title>Disobedience: Introduction</title>
+   <link rel=stylesheet
+         type="text/css"
+         href="disobedience.css">
+ </head>
+ <body>
+   <h1>1. Introduction</h1>
+
+   <p>This chapter covers the following topics:</p>
+
+   <ul>
+     <li>What DisOrder and Disobedience are, and how to get them</li>
+     <li>How to get a DisOrder login</li>
+     <li>How to start Disobedience</li>
+   </ul>
+
+   <h2><a name=whatis>1.1 What is DisOrder?</a></h2>
+
+   <p><a href="http://www.greenend.org.uk/rjk/disorder/">DisOrder</a>
+   is a multi-user software jukebox.  It allows MP3s, OGGs, etc to be
+   played either using a single sound card or over a network to many
+   different computers, with multiple different people controlling
+   what is played.</p>
+
+   <p>DisOrder has three main user interfaces.</p>
+
+   <ul>
+     <li>It has a command-line interface, suitable for ad-hoc use and
+     scripting.</li>
+
+     <li>It has a web interface, usable with graphical web browsers
+     (Firefox, Internet Explorer etc).</li>
+
+     <li>It has a graphical client called Disobedience.</li>
+   </ul>
+
+   <p>This manual is about Disobedience, so it does not really cover
+   installation or management issues.  However in this chapter it will
+   cover a few such topics as they are necessary to getting up and
+   running with Disobedience.</p>
+
+   <!--
+
+   <p>This diagram shows an overview of one possible setup.</p>
+
+   <p class=image><img src="arch-simple.png"></p>
+
+   <p>The server and web interface run on one computer.  Disobedience
+   runs on a desktop computer and accesses the server via the network.
+   On another system the RTP player runs and plays sound received from
+   the server via its local sound card.</p>
+
+   <p>Many other configurations are possible.  For instance the server
+   could play directly to a local soundcard.  Also if Disobedience
+   runs on the same computer as the RTP player then it can be used to
+   stop and start the player.  Of course Disobedience can also be run
+   on the same computer as the server provided it can run X11
+   applications.</p>
+
+   -->
+
+   <h2><a name=getting>1.2 Getting DisOrder</a></h2>
+
+   <p>There are two ways to get DisOrder.</p>
+
+   <p>If you have a Debian system you can download the <tt>.deb</tt>
+   files <a href="http://www.greenend.org.uk/rjk/disorder/">from
+   DisOrder's home page</a> and install those.  There are four
+   packages to choose from:</p>
+
+   <ul>
+     <li><tt>disorder.deb</tt> - the base package.  You should always
+     install this.  It contains the command-line client.</li>
+
+     <li><tt>disorder-server.deb</tt> - the server and web interface.
+     Only install this if you are setting up a totally new DisOrder
+     installation.  If you just want to access an existing one, you
+     don't need this package.</li>
+
+     <li><tt>disobedience.deb</tt> - the graphical client.  If you are
+     reading this manual you want this package!</li>
+
+     <li><tt>disorder-rtp.deb</tt> - the network play client.  If your
+     server is set up to transmit sound over the network you will need
+     this.  If it uses a local sound card then this package won't be
+     useful to you.</li>
+
+   </ul>
+
+   <p>(At the time of writing, DisOrder is not included as part of
+   Debian.)</p>
+
+   <p>If you have another kind of Linux system, or a Mac, you must
+   build from source code.  See the <tt>README</tt> file included in
+   the source distribution for more details.  Note that to use
+   Disobedience on a Mac, you will need X11.app.</p>
+
+   <p>There is no Windows support (although the web interface can be
+   used from Windows computers).</p>
+
+   <h2><a name=createlogin>1.3 Getting a DisOrder login</a></h2>
+
+   <p>The easiest way to get a DisOrder login is to access the web
+   interface and set one up using that.  To do this,
+   visit <tt>http://HOSTNAME/cgi-bin/disorder</tt>,
+   where <tt>HOSTNAME</tt> is the name of the server where DisOrder is
+   installed.  You should then be able to select the <b>Login</b>
+   option at the top of the screen.</p>
+
+   <p class=image><img src="disorder-web-login.png"></p>
+
+   <p>Go to the <b>New Users</b> form and enter the username you want
+   to use, your email address, and a password.  The password must be
+   entered twice to verify you did not mistype it (since it won't be
+   displayed on the screen).  When you press <b>Register</b>, you will
+   be sent an email requiring you to confirm your registration.</p>
+
+   <p class=image><img src="disorder-email-confirm.png"></p>
+
+   <p>Your login won't be active until you click on this URL.</p>
+
+   <p>(It might be that your installation isn't set up to allow
+   automatic registration.  In that case the local sysadmin will have
+   to create your login and set your initial password by hand.)</p>
+
+   <p>Having done this you could of course just use the web interface.
+   But since this is the manual for Disobedience, it is assumed that
+   you want to take advantage of its more convenient design and extra
+   features.</p>
+
+   <h2><a name=starting>1.4 Starting Disobedience</a></h2>
+
+   <p>On Debian systems it should be possible to find Disobedience in
+   the menu system:</p>
+
+   <p class=image><img src="disobedience-debian-menu.png"></p>
+
+   <p>On other systems you will have to start it from the command line
+   by typing its name at a command prompt.  You can (optionally) use
+   an <tt>&amp;</tt> suffix to stop it tying up your terminal.</p>
+
+   <p class=image><img src="disobedience-terminal.png"></p>
+
+   <p>(Please note that Disobedience shouldn't write any messages to
+   the terminal.  If it does that probably indicates a bug, which
+   should <a href="http://code.google.com/p/disorder/issues/list">be
+   reported</a>.)</p>
+
+   <h2><a name=login>1.5 Initial Login</a></h2>
+
+   <p>The first time you run Disobedience it won't know what server to
+   connect to, your username or your password, and will therefore
+   display a login box.</p>
+
+   <p class=image><img src="login.png"></p>
+
+   <p>If Disobedience is running on a different computer to the
+   server, then you should make sure the <b>Remote</b> box is ticked
+   and fill in the host name (or IP address) and port number
+   (&ldquo;Service&rdquo;).  If you don't know what values to use
+   here, ask your local sysadmin.  If, on the other hand, Disobedience
+   is running on the <i>same</i> computer as the server then you can
+   leave the <b>Remote</b> box clear and it should be able to connect
+   to it without using the network.</p>
+
+   <p>In any case, you will need to enter your username and
+   password, as set up earlier.</p>
+
+   <p>Once you have logged in successfuly, Disobedience will remember
+   these login settings, so it should not be necessary to enter them
+   again.  If you need to change them for any reason, you can either
+   select the <b>Server > Login</b> option to bring the login window
+   back, or (if you prefer), edit the file <tt>~/.disorder/passwd</tt>
+   directly.</p>
+
+   <hr>
+
+   <a href="index.html">Back to contents</a>
+
+ </body>
+</html>
diff --git a/disobedience/manual/login.png b/disobedience/manual/login.png
new file mode 100644 (file)
index 0000000..9f3b074
Binary files /dev/null and b/disobedience/manual/login.png differ
diff --git a/disobedience/manual/menu-control.png b/disobedience/manual/menu-control.png
new file mode 100644 (file)
index 0000000..c2da17b
Binary files /dev/null and b/disobedience/manual/menu-control.png differ
diff --git a/disobedience/manual/menu-edit.png b/disobedience/manual/menu-edit.png
new file mode 100644 (file)
index 0000000..7cdb83b
Binary files /dev/null and b/disobedience/manual/menu-edit.png differ
diff --git a/disobedience/manual/menu-help.png b/disobedience/manual/menu-help.png
new file mode 100644 (file)
index 0000000..4ecc391
Binary files /dev/null and b/disobedience/manual/menu-help.png differ
diff --git a/disobedience/manual/menu-server.png b/disobedience/manual/menu-server.png
new file mode 100644 (file)
index 0000000..5502bf5
Binary files /dev/null and b/disobedience/manual/menu-server.png differ
diff --git a/disobedience/manual/misc.html b/disobedience/manual/misc.html
new file mode 100644 (file)
index 0000000..b65b6fa
--- /dev/null
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<!--
+This file is part of DisOrder.
+Copyright (C) 2009 Richard Kettlewell
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<html>
+ <head>
+   <title>Disobedience: Appendix</title>
+   <link rel=stylesheet
+         type="text/css"
+         href="disobedience.css">
+ </head>
+ <body>
+   <h1>Appendix</h1>
+
+   <h2><a name=netplay>Network Play</a></h2>
+
+   <p>Network play uses a background copy
+   of <tt>disorder-playrtp</tt>.  If you quit Disobedience the
+   player will continue playing and can be disabled from a later
+   run of Disobedience.</p>
+
+   <p>The player will log to <tt>~/.disorder/HOSTNAME-rtp.log</tt> so
+   look there if it does not seem to be working.</p>
+
+   <p>You can stop it without running Disobedience by the command
+   <tt>killall disorder-playrtp</tt>.</p>
+
+   <h2><a name=bugs>Reporting Bugs</a></h2>
+
+   <p>Please report bugs using
+   DisOrder's <a href="http://code.google.com/p/disorder/issues/list">bug
+   tracker</a>.</p>
+
+   <p>Known problems include:</p>
+
+   <ul>
+
+     <li>There is no particular provision for multiple users of the
+     same computer sharing a single <tt>disorder-playrtp</tt> process.
+     This shouldn't be too much of a problem in practice but something
+     could perhaps be done given demand.</li>
+
+     <li>Try to do remote user management when the server is
+     configured to refuse this produces rather horrible error
+     behavior.</li>
+
+     <li>Resizing columns doesn't work very well.  This is a GTK+
+     bug.</li>
+
+   </ul>
+
+   <h2><a name=copyright>Copyright Notice</a></h2>
+
+   <p>Copyright &copy; 2003-2009 <a
+   href="http://www.greenend.org.uk/rjk/">Richard Kettlewell</a><br>
+
+   Portions copyright &copy; 2007 <a
+   href="http://www.chiark.greenend.org.uk/~ryounger/">Ross
+   Younger</a><br>
+
+   Portions copyright &copy; 2007, 2008 Mark Wooding<br>
+
+   Portions extracted from <a
+   href="http://mpg321.sourceforge.net/">MPG321</a>, Copyright &copy; 2001 Joe
+   Drew, Copyright &copy; 2000-2001 Robert Leslie<br>
+
+   Portions copyright &copy; 1997-2006 <a
+   href="http://www.fsf.org/">Free Software Foundation, Inc</a><br>
+   
+   Portions Copyright &copy; 2000 <a href="http://www.redhat.com">Red Hat,
+   Inc.</a>, Jonathan Blandford <jrb@redhat.com></p>
+
+   <p>This program is free software: you can redistribute it and/or modify it
+   under the terms of the <a href="http://www.gnu.org/licenses/gpl-3.0.html">GNU
+   General Public License</a> as published by the Free Software Foundation,
+   either version 3 of the License, or (at your option) any later version.</p>
+
+   <p>This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.</p>
+
+   <p>You should have received a copy of the GNU General Public License along
+   with this program.  If not, see <a
+   href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.</p>
+
+   <hr>
+
+   <a href="index.html">Back to contents</a>
+
+ </body>
+</html>
diff --git a/disobedience/manual/playlist-create.png b/disobedience/manual/playlist-create.png
new file mode 100644 (file)
index 0000000..f48997d
Binary files /dev/null and b/disobedience/manual/playlist-create.png differ
diff --git a/disobedience/manual/playlist-picker-menu.png b/disobedience/manual/playlist-picker-menu.png
new file mode 100644 (file)
index 0000000..be5a65b
Binary files /dev/null and b/disobedience/manual/playlist-picker-menu.png differ
diff --git a/disobedience/manual/playlist-popup-menu.png b/disobedience/manual/playlist-popup-menu.png
new file mode 100644 (file)
index 0000000..0bb673d
Binary files /dev/null and b/disobedience/manual/playlist-popup-menu.png differ
diff --git a/disobedience/manual/playlist-window.png b/disobedience/manual/playlist-window.png
new file mode 100644 (file)
index 0000000..9ffd991
Binary files /dev/null and b/disobedience/manual/playlist-window.png differ
diff --git a/disobedience/manual/playlists.html b/disobedience/manual/playlists.html
new file mode 100644 (file)
index 0000000..bef0c18
--- /dev/null
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<!--
+This file is part of DisOrder.
+Copyright (C) 2009 Richard Kettlewell
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<html>
+ <head>
+   <title>Disobedience: Playlists</title>
+   <link rel=stylesheet
+         type="text/css"
+         href="disobedience.css">
+ </head>
+ <body>
+   <h1>5. Playlists</h1>
+
+   <p>The chapter describes playlist and how to use them.</p>
+
+   <h2><a name=what>5.1 What are playlists?</a></h2>
+
+   <p>A playlist is a named list of tracks stored by the server.  It
+   can be edit by either just its owner or, alternatively, by all
+   users.  It can be played as a unit when required.</p>
+
+   <p>Playlists fall into three categories:</p>
+
+   <table>
+     <tr>
+       <td><b>Shared</b></td>
+       <td>Shared playlists have no owner and can be seen and edited
+       by anybody.</td>
+     </tr>
+
+     <tr>
+       <td><b>Public</b></td>
+       <td>Public playlist are owned by their creator and can be seen
+       by anybody.  Only their creator can edit them, however.</td>
+     </tr>
+
+     <tr>
+       <td><b>Private</b></td>
+       <td>Private playlists are owned by their creator and can only
+       be seen or edited by their creator.</td>
+     </tr>
+
+   </table>
+
+   <p>To bring up the playlist window, select <b>Edit > Edit
+   Playlists</b> from the menu.</p>
+
+   <h2><a name=creating>5.2 Creating Playlists</a></h2>
+
+   <p>To create a playlist, click the <b>Add</b> button in the
+   playlist editor.  This will create a pop-up window where you can
+   enter the name of the new playlist and decide whether it is
+   shared, public or private.</p>
+
+   <p>Only Roman letters (without any accents) and digits are allowed
+   in playlist names.</p>
+
+   <p class=image><img src="playlist-create.png"></p>
+
+   <h2><a name=editing>5.3 Editing Playlists</a></h2>
+
+   <p>You can select the playlist to edit from the left side of the
+   window.  Shared playlists are listed first, then the public
+   playlists of each user (plus private playlists that you own).</p>
+
+   <p class=image><img src="playlist-window.png"></p>
+
+   <p>When a playlist is selected, you can edit in the right hand half
+   of the playlist window.  The editor is very similar in structure
+   and function to the <a href="tabs.html#queue">queue</a>.  You can
+   rearrange tracks within it by drag and drop and you can drag tracks
+   from the <b>Choose</b> tab into it.</p>
+
+   <p>Right clicking will create a pop-up menu which you can use to
+   play indivudual tracks or the whole playlist, as well as the
+   usual options as found in the <b>Queue</b> tab.</p>
+
+   <p class=image><img src="playlist-popup-menu.png"></p>
+
+   <h2><a name=playing>5.3 Playing Playlists</a></h2>
+
+   <p>There are three ways to play a playlist:</p>
+
+   <ol>
+
+     <li>You can use <b>Control > Activate Playlist</b> from the main
+     menu.</li>
+
+     <li>Right clicking in either half of the playlist editor will
+     create a pop-up menu.  There is an option to play this
+     playlist.</li>
+
+     <li>You can select all the tracks and drag them to the desired
+     point in the <b>Queue</b> tab.</li>
+
+   </ol>
+
+   <hr>
+
+   <a href="index.html">Back to contents</a>
+
+ </body>
+</html>
diff --git a/disobedience/manual/properties.html b/disobedience/manual/properties.html
new file mode 100644 (file)
index 0000000..34d215f
--- /dev/null
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<!--
+This file is part of DisOrder.
+Copyright (C) 2009 Richard Kettlewell
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<html>
+ <head>
+   <title>Disobedience: Track Properties</title>
+   <link rel=stylesheet
+         type="text/css"
+         href="disobedience.css">
+ </head>
+ <body>
+   <h1>4. Track Properties</h1>
+
+   <p>The chapter describes how to edit track properties.</p>
+
+   <p class=image><img src="track-properties.png"></p>
+
+   <p>This window can be invoked from any of the four tabs by
+   selecting one or more tracks and then either selected <b>Edit >
+   Track Properties</b> or via the right-click pop-up menu.</p>
+
+   <h2><a name=names>4.1 Track Name Parts</a></h2>
+
+   <p>The first three fields for each track are the parts of its name:
+   the artist, album and title.  These control what appear in the
+   similarly named columns in the queue and other tabs.  If they are
+   wrong then you can edit them here to correct them.</p>
+
+   <p>The double-headed arrow at the right of each field will copy the
+   current field value to all the other tracks in the window.  For
+   instance if an album name is mis-spelled or wrong then you could
+   follow the following procedure to correct it for all its
+   tracks:</p>
+
+   <ol>
+
+     <li>In the <b>Choose</b> tab, select all the tracks in the album.</li>
+
+     <li>Select <b>Edit > Track Properties</b> to bring up the track
+     properties window.</li>
+
+     <li>Edit the album name in the first track.</li>
+
+     <li>Click the arrow button to the right of the corrected version.</li>
+
+     <li>Click the <b>OK</b> button.</li>
+
+   </ol>
+
+   <h2><a name=tags>4.2 Tags</a></h2>
+
+   <p>Each track has an associated collection of tags.  These can used
+   when searching for tracks in the <b>Choose</b> tab or to control
+   which tracks are picked at random (although this functionality is
+   not readily available in current versions of Disobedience).</p>
+
+   <p>To add tags to a track enter the tags you want to apply to it in
+   the <b>Tags</b> field, separated by commas.  Tags cannot contain
+   commas and are compared without regard to whitespace.</p>
+
+   <h2><a name=weight>4.3 Track Weight</a></h2>
+
+   <p>Every track has an associated weight.  A higher weight makes the
+   track more likely to be picked at random and lower weight makes it
+   less likely to be picked at random.  (In the simplest case the
+   probability that it will be picked is equal to its weight divided
+   by the total weight of all tracks, although there are a number of
+   other factors that modify this.)</p>
+
+   <p>If no weight has been explicitly set then the track gets a
+   default weight of 90,000.</p>
+
+   <p>One way to prevent a track being picked at random would be to
+   set its weight to zero, but in fact there is a box you can untick
+   to suppress random selection of tracks too.</p>
+
+   <hr>
+
+   <a href="index.html">Back to contents</a>
+
+ </body>
+</html>
diff --git a/disobedience/manual/queue-menu.png b/disobedience/manual/queue-menu.png
new file mode 100644 (file)
index 0000000..af2dff9
Binary files /dev/null and b/disobedience/manual/queue-menu.png differ
diff --git a/disobedience/manual/queue.png b/disobedience/manual/queue.png
new file mode 100644 (file)
index 0000000..224fc71
Binary files /dev/null and b/disobedience/manual/queue.png differ
diff --git a/disobedience/manual/queue2.png b/disobedience/manual/queue2.png
new file mode 100644 (file)
index 0000000..ff5cbed
Binary files /dev/null and b/disobedience/manual/queue2.png differ
diff --git a/disobedience/manual/recent.png b/disobedience/manual/recent.png
new file mode 100644 (file)
index 0000000..8ee5e68
Binary files /dev/null and b/disobedience/manual/recent.png differ
diff --git a/disobedience/manual/tabs.html b/disobedience/manual/tabs.html
new file mode 100644 (file)
index 0000000..ea63a37
--- /dev/null
@@ -0,0 +1,185 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<!--
+This file is part of DisOrder.
+Copyright (C) 2009 Richard Kettlewell
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<html>
+ <head>
+   <title>Disobedience: Tabs</title>
+   <link rel=stylesheet
+         type="text/css"
+         href="disobedience.css">
+ </head>
+ <body>
+   <h1>3. Tabs</h1>
+
+   <p>The chapter contains detailed descriptions of the Queue, Recent,
+   Choose and Added tabs.</p>
+
+   <h2><a name=queue>3.1 The Queue</a></h2>
+
+   <p>The <b>Queue</b> tab has already
+   been <a href="window.html#main">briefly described</a>, but there
+   are some more things to say about.  To start with, the meaning of
+   the columns:</p>
+
+   <table>
+     <tr>
+       <td><b>When</b></td>
+       <td>This is the server's estimate of when the track will start.
+       If play is disabled but the queue isn't empty, then it is based
+       on the assumption that play is about to be enabled.</td>
+     </tr>
+     <tr>
+       <td><b>Who</b></td>
+       <td>This is the person who selected the track.  If the track
+       was picked at random by the server it is empty.  You can
+       &ldquo;adopt&rdquo; a randomly picked track; see below.</td>
+     </tr>
+     <tr>
+       <td><b>Artist</b></td>
+       <td>The artist responsible for the track.  Like the other track
+       name columns, this depends on the server's logic for parsing
+       filename, so can be a bit wrong.  You can edit the track
+       properties to correct it.</td>
+     </tr>
+     <tr>
+       <td><b>Album</b></td>
+       <td>The album that the track came from.</td>
+     </tr>
+     <tr>
+       <td><b>Title</b></td>
+       <td>The title of the track.</td>
+     </tr>
+     <tr>
+       <td><b>Length</b></td>
+       <td>The length the track will play for.  For the playing track
+       this will include the amount of time it's been playing so
+       far.</td>
+     </tr>
+   </table>
+
+   <p>You can select tracks in the queue by clicking on them.  You can
+   select multiple tracks by clicking in a second location with SHIFT
+   depressed (to select all tracks between the first and second click)
+   or with CTRL depressed (to add a single track to the selection).</p>
+
+   <p>Having selected tracks you can drag them to a new location in
+   the queue.  Of course, you can't drag the playing track, nor can
+   you drag other tracks before it.</p>
+
+   <p>Right-clicking in the queue will create a pop-up menu:</p>
+
+   <p class=image><img src="queue-menu.png"></p>
+
+   <p><b>Track Properties</b> will create a window with editable
+   properties of each selected track.  <b>Scratch playing track</b>
+   only works if the playing track is the selected track and will stop
+   it playing.  <b>Remove track from queue</b> will remove the
+   selected (non-playing) tracks from the queue.</p>
+
+   <p><b>Adopt track</b> will apply your name to one without an entry
+   in the <b>Who</b> column, i.e. one that was randomly picked by the
+   server.  The reason you might do this is to signal to other users
+   that you did want this track to play.  (For instance, it might be
+   an accepted convention that randomly picked tracks were fair game
+   for removal but that tracks picked by a human should normally be
+   left alone.)</p>
+
+   <h2><a name=recent>3.2 Recently Played Tracks</a></h2>
+
+   <p class=image><img src="recent.png"></p>
+
+   <p>The <b>Recent</b> tab is similar in structure to the queue but
+   it shows tracks that have played recently.  The <b>When</b> column
+   indicates when the track played rather than when it will
+   played.</p>
+
+   <p>Right clicking will create a pop-up menu with similar options to
+   those found in the queue's equivalent menu.  The one additional
+   option is <b>Play track</b>, which allows a recently played track
+   to be added back to the queue.</p>
+
+   <p>The other way of adding tracks from this tab back to the queue
+   is to drag them to the queue tab and then to the desired point into
+   the queue.</p>
+
+   <h2><a name=choose>3.3 Choosing Tracks</a></h2>
+
+   <p>The <b>Choose</b> tab contains all the tracks known to the
+   server, organized into a hierarchical structure based on the
+   underlying file and directory structure.</p>
+
+   <p class=image><img src="choose.png"></p>
+
+   <p>The boxes in the <b>Queued</b> column are ticked if the track is
+   somewhere in the queue.  You can click on an unticked box to add
+   the track to the queue, but clicking an already-ticked one will
+   have no effect.</p>
+
+   <p>Directories can be expanded or collapsed by clicking on the
+   triangular signs left of their names.  Tracks can be selected and
+   deselected by clicking on them.  You can select multiple tracks by
+   clicking in a second location with SHIFT depressed (to select all
+   tracks between the first and second click) or with CTRL depressed
+   (to add a single track to the selection).</p>
+
+   <p>Right clicking will create a pop-up menu with what are hopefuly
+   now familiar options.  <b>Play track</b> will add the selected
+   track(s) to the queue and <b>Track Properties</b> will create a
+   window with editable properties of each selected track.</p>
+
+   <p>Note that when tracks are added to the queue these ways they
+   will be added before any tracks picked at random by the server, so
+   that users don't have to wait for them to play out.</p>
+
+   <p>Selected tracks can also be dragged to the queue, by dragging
+   first to the <b>Queue</b> tab itself and then to the desired
+   location in the queue.</p>
+
+   <p class=image><img src="choose-search.png"></p>
+
+   <p>To do a word search over all tracks, you can just start typing.
+   Your search terms will appear in the input box at the bottom of the
+   window.  Directories containing matching tracks are automatically
+   opened and the matches highlighted in yellow.  You can jump to the
+   previous or next search result with the up and down arrows at the
+   bottom right of the screen (or jump to the next one by pressing
+   CTRL+G).</p>
+
+   <p>If you enter more than one word at a time then only tracks which
+   match <i>both</i> words will be listed.</p>
+
+   <p>You can search for <a href="properties.html#tags">tags</a> as
+   well as words.  For instance to search for the tag
+   &ldquo;happy&rdquo; you would enter <tt>tag:happy</tt> in the
+   search box.</p>
+
+   <p>To clear the search, press the <b>Cancel</b> button.</p>
+
+   <h2><a name=added>3.4 Newly Added Tracks</a></h2>
+
+   <p>The <b>Added</b> tab shows tracks that have been newly detected
+   by the server, in order to allow those tracks to be conveniently
+   played.  In behavior it is the same as the
+   recently <a href="#recent">played tracks list</a>.</p>
+
+   <hr>
+
+   <a href="index.html">Back to contents</a>
+
+ </body>
+</html>
diff --git a/disobedience/manual/track-properties.png b/disobedience/manual/track-properties.png
new file mode 100644 (file)
index 0000000..27093b8
Binary files /dev/null and b/disobedience/manual/track-properties.png differ
diff --git a/disobedience/manual/volume-slider.png b/disobedience/manual/volume-slider.png
new file mode 100644 (file)
index 0000000..c7e25c5
Binary files /dev/null and b/disobedience/manual/volume-slider.png differ
diff --git a/disobedience/manual/window.html b/disobedience/manual/window.html
new file mode 100644 (file)
index 0000000..72fe5f7
--- /dev/null
@@ -0,0 +1,164 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<!--
+This file is part of DisOrder.
+Copyright (C) 2009 Richard Kettlewell
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<html>
+ <head>
+   <title>Disobedience: Window Layout</title>
+   <link rel=stylesheet
+         type="text/css"
+         href="disobedience.css">
+ </head>
+ <body>
+   <h1>2. Window Layout</h1>
+
+   <p>This chapter contains a tour of the main Disobedience
+   window.</p>
+
+   <h2><a name=main>2.1 The Disobedience Window</a></h2>
+   
+   <p>Disobedience should look something like this when you've started
+   it up and logged in:</p>
+
+   <p class=image><img src="queue.png"></p>
+
+   <p>At the top is the menu bar, and immediately below it a toolbar
+   with some buttons and a volume control.  (In some cases the volume
+   control may be absent.)  Below that is a row of
+   tabs: <b>Queue</b>, <b>Recent</b> and so on.  The <b>Queue</b> tab
+   is selected: this displays the currently playing track and the list
+   of tracks that will play in the near future.</p>
+
+   <p>In this example nothing is actually playing.  (Apart from the
+   fact that you wouldn't hear anything) you can tell this because the
+   top row only has a single length indicator (&ldquo;3:04&rdquo;).
+   If it was playing it would show how far through the track it was
+   too.  Secondly, all the tracks were chosen at random.  You can tell
+   this because the <b>Who</b> column is empty.
+
+   <p>In the screenshot below both of these things have changed.
+   Use <tt>rjk</tt> has selected some tracks and the first of them is
+   playing.</p>
+
+   <p class=image><img src="queue2.png"></p>
+
+   <p>The playing track is highlighted with a green backgroud.  It's 4
+   minutes and 10 seconds long and so far, the first 21 seconds of it
+   has played.</p>
+
+   <h2><a name=buttons>2.2 Buttons</a></h2>
+
+   <p>The meaning of the buttons is as follows:</p>
+
+   <table>
+     <tr>
+       <td><img src="button-pause.png"></td>
+       <td>The pause button.  This only effective when a track is
+       playing.  When it is pressed the playing track is paused.</td>
+     </tr>
+
+     <tr>
+       <td><img src="button-scratch.png"></td>
+       <td>The scratch button.  As above this is only effective when
+       a track is playing.  Pressing it will stop that track playing
+       and move onto the next one.</td>
+     </tr>
+
+     <tr>
+        <td><img src="button-random.png"></td>
+
+        <td>The random play button.  Random play, which is enabled
+        when the button is depressed, means that if
+        nothing is playing the server will automatically pick a track
+        at random and play that.  Furthermore it will ensure that the
+        queue always has a minimum number of tracks, so you can see
+        ahead of time what will play next.</td>
+     </tr>
+
+     <tr>
+        <td><img src="button-playing.png"></td>
+        <td>The playing button.  Normally this would always be left
+        enabled, i.e. with the button depressed.
+         If it is disabled then nothing will be
+        played at all, regardless of whether it was randomly chosen
+        or picked by a human.</td>
+     </tr>
+
+     <tr>
+        <td><img src="button-rtp.png"></td>
+        <td>The network play button.  This is only effective if the
+        server is playing over the network (as opposed to using a
+        local sound card).  When network play is enabled,
+        Disobedience runs a client program on your computer to play
+        sound received over the network using your sound card.</td>
+     </tr>
+   </table>
+
+   <p>To the right of the buttons are two sliders:</p>
+
+   <p class=image><img src="volume-slider.png"></p>
+
+   <p>The left hand slider is the volume control.  This ranges from 0
+   (silent) to 10 (loudest).  The right hand slider is the balance
+   control, which ranges form -1.0 (left) to +1.0 (right).</p>
+
+   <h2><a name=menu>2.3 The Menu Bar</a></h2>
+
+   <p class=image><img src="menu-server.png"></p>
+
+   <p>The <b>Server</b> menu is loosely analogous to the <b>File</b>
+   menu in othe applications.  It has no file-related options though,
+   so the name would not be appropriate!  Instead the three options it
+   has are <b>Login</b>, which brings back the login window shown the
+   first time it is run, <b>Manage Users</b> which allows
+   adminstrators to do user management, and <b>Quit</b>.</p>
+
+   <!-- TODO link to user management -->
+
+   <p class=image><img src="menu-edit.png"></p>
+
+   <p>The <b>Edit</b> menu has options to select or deselect all
+   tracks.  You can also do this with CTRL-A and CTRL-SHIFT-A, and of
+   course you can select tracks in the queue (or other tabs) with the
+   mouse.</p>
+
+   <p>The <b>Track Properties</b> option will create a window with
+   editable <a href="properties.html">properties</a> of each selected
+   track and the <b>Edit Playlists</b> option will create a window
+   allowing editing of <a href="playlists.html">playlists</a>.</p>
+
+   <p class=image><img src="menu-control.png"></p>
+
+   <p>The <b>Control</b> menu options are mostly equivalent to the
+   buttons described above.  The exceptions is <b>Activate
+   Playlist</b> which allows you to play
+   a <A href="playlists.html">playlist</a>, and <b>Compact Mode</b>
+   which switches Disobedience's window to a smaller format.</p>
+
+   <p class=image><img src="menu-help.png"></p>
+
+   <p>The <b>Help</b> menu has an option to bring up the Disobedience
+   manual and an <b>About</b> option which will display a bit of
+   version information for the server and for Disobedience (which
+   might not be the same).</p>
+
+   <hr>
+
+   <a href="index.html">Back to contents</a>
+
+ </body>
+</html>
index 283c0d1..bae0f00 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2006-2008 Richard Kettlewell
+ * Copyright (C) 2006-2009 Richard Kettlewell
  *
  * 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
 
 #include "disobedience.h"
 
+static void toggled_minimode(GtkCheckMenuItem *item, gpointer userdata);
+
 static GtkWidget *selectall_widget;
 static GtkWidget *selectnone_widget;
 static GtkWidget *properties_widget;
+GtkWidget *menu_playlists_widget;
+GtkWidget *playlists_menu;
+GtkWidget *menu_editplaylists_widget;
+static GtkWidget *menu_minimode_widget;
 
 /** @brief Main menu widgets */
 GtkItemFactory *mainmenufactory;
 
+/** @brief Set for full mode, clear for mini mode */
+int full_mode = 1;
+
 static void about_popup_got_version(void *v,
                                     const char *err,
                                     const char *value);
@@ -113,7 +122,7 @@ static void edit_menu_show(GtkWidget attribute((unused)) *widget,
                              && t->selectnone_sensitive(t->extra));
   }
 }
-   
+
 /** @brief Fetch version in order to display the about... popup */
 static void about_popup(gpointer attribute((unused)) callback_data,
                         guint attribute((unused)) callback_action,
@@ -131,7 +140,7 @@ static void manual_popup(gpointer attribute((unused)) callback_data,
                        GtkWidget attribute((unused)) *menu_item) {
   D(("manual_popup"));
 
-  popup_help();
+  popup_help(NULL);
 }
 
 /** @brief Called when version arrives, displays about... popup */
@@ -173,7 +182,7 @@ static void about_popup_got_version(void attribute((unused)) *v,
                      FALSE/*fill*/,
                      1/*padding*/);
   gtk_box_pack_start(GTK_BOX(vbox),
-                     gtk_label_new("\xC2\xA9 2004-2008 Richard Kettlewell"),
+                     gtk_label_new("\xC2\xA9 2004-2009 Richard Kettlewell"),
                      FALSE/*expand*/,
                      FALSE/*fill*/,
                      1/*padding*/);
@@ -271,15 +280,15 @@ GtkWidget *menubar(GtkWidget *w) {
     },
     {
       (char *)"/Edit/Select all tracks", /* path */
-      0,                                /* accelerator */
+      (char *)"<CTRL>A",                /* accelerator */
       menu_tab_action,                  /* callback */
       offsetof(struct tabtype, selectall_activate), /* callback_action */
-      0,                                /* item_type */
-      0                                 /* extra_data */
+      (char *)"<StockItem>",           /* item_type */
+      GTK_STOCK_SELECT_ALL,            /* extra_data */
     },
     {
       (char *)"/Edit/Deselect all tracks", /* path */
-      0,                                /* accelerator */
+      (char *)"<CTRL><SHIFT>A",         /* accelerator */
       menu_tab_action,                  /* callback */
       offsetof(struct tabtype, selectnone_activate), /* callback_action */
       0,                                /* item_type */
@@ -290,10 +299,19 @@ GtkWidget *menubar(GtkWidget *w) {
       0,                                /* accelerator */
       menu_tab_action,                  /* callback */
       offsetof(struct tabtype, properties_activate), /* callback_action */
+      (char *)"<StockItem>",            /* item_type */
+      GTK_STOCK_PROPERTIES,             /* extra_data */
+    },
+    {
+      (char *)"/Edit/Edit playlists",   /* path */
+      0,                                /* accelerator */
+      playlist_window_create,           /* callback */
+      0,                                /* callback_action */
       0,                                /* item_type */
       0                                 /* extra_data */
     },
     
+    
     {
       (char *)"/Control",               /* path */
       0,                                /* accelerator */
@@ -307,8 +325,8 @@ GtkWidget *menubar(GtkWidget *w) {
       (char *)"<CTRL>S",                /* accelerator */
       0,                                /* callback */
       0,                                /* callback_action */
-      0,                                /* item_type */
-      0                                 /* extra_data */
+      (char *)"<StockItem>",            /* item_type */
+      GTK_STOCK_STOP,                   /* extra_data */
     },
     {
       (char *)"/Control/Playing",       /* path */
@@ -334,7 +352,23 @@ GtkWidget *menubar(GtkWidget *w) {
       (char *)"<CheckItem>",            /* item_type */
       0                                 /* extra_data */
     },
-    
+    {
+      (char *)"/Control/Compact mode",  /* path */
+      (char *)"<CTRL>M",                /* accelerator */
+      0,                                /* callback */
+      0,                                /* callback_action */
+      (char *)"<CheckItem>",            /* item_type */
+      0                                 /* extra_data */
+    },
+    {
+      (char *)"/Control/Activate playlist", /* path */
+      0,                                /* accelerator */
+      0,                                /* callback */
+      0,                                /* callback_action */
+      (char *)"<Branch>",               /* item_type */
+      0                                 /* extra_data */
+    },
+
     {
       (char *)"/Help",                  /* path */
       0,                                /* accelerator */
@@ -344,12 +378,12 @@ GtkWidget *menubar(GtkWidget *w) {
       0                                 /* extra_data */
     },
     {
-      (char *)"/Help/Manual page",      /* path */
+      (char *)"/Help/Manual",           /* path */
       0,                                /* accelerator */
       manual_popup,                     /* callback */
       0,                                /* callback_action */
-      0,                                /* item_type */
-      0                                 /* extra_data */
+      (char *)"<StockItem>",            /* item_type */
+      GTK_STOCK_HELP,                   /* extra_data */
     },
     {
       (char *)"/Help/About DisOrder",   /* path */
@@ -378,23 +412,45 @@ GtkWidget *menubar(GtkWidget *w) {
                                                 "<GdisorderMain>/Edit/Deselect all tracks");
   properties_widget = gtk_item_factory_get_widget(mainmenufactory,
                                                  "<GdisorderMain>/Edit/Track properties");
+  menu_playlists_widget = gtk_item_factory_get_item(mainmenufactory,
+                                               "<GdisorderMain>/Control/Activate playlist");
+  playlists_menu = gtk_item_factory_get_widget(mainmenufactory,
+                                               "<GdisorderMain>/Control/Activate playlist");
+  menu_editplaylists_widget = gtk_item_factory_get_widget(mainmenufactory,
+                                                     "<GdisorderMain>/Edit/Edit playlists");
+  menu_minimode_widget = gtk_item_factory_get_widget(mainmenufactory,
+                                                     "<GdisorderMain>/Control/Compact mode");
   assert(selectall_widget != 0);
   assert(selectnone_widget != 0);
   assert(properties_widget != 0);
+  assert(menu_playlists_widget != 0);
+  assert(playlists_menu != 0);
+  assert(menu_editplaylists_widget != 0);
 
-  
   GtkWidget *edit_widget = gtk_item_factory_get_widget(mainmenufactory,
                                                        "<GdisorderMain>/Edit");
   g_signal_connect(edit_widget, "show", G_CALLBACK(edit_menu_show), 0);
-  
+
   event_register("rights-changed", menu_rights_changed, 0);
   users_set_sensitive(0);
   m = gtk_item_factory_get_widget(mainmenufactory,
                                   "<GdisorderMain>");
   set_tool_colors(m);
+  if(menu_minimode_widget)
+    g_signal_connect(G_OBJECT(menu_minimode_widget), "toggled",
+                     G_CALLBACK(toggled_minimode), NULL);
   return m;
 }
 
+static void toggled_minimode(GtkCheckMenuItem  *item,
+                             gpointer attribute((unused)) userdata) {
+  int new_full_mode = !gtk_check_menu_item_get_active(item);
+  if(full_mode != new_full_mode) {
+    full_mode = new_full_mode;
+    event_raise("mini-mode-changed", NULL);
+  }
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index c807a27..a1fdf4d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder
- * Copyright (C) 2006-2008 Richard Kettlewell
+ * Copyright (C) 2006-2008, 2010 Richard Kettlewell
  *
  * 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
@@ -27,7 +27,7 @@ struct image {
   const guint8 *data;
 };
 
-#include "images.h"
+#include "../images/images.h"
 
 /* Miscellaneous GTK+ stuff ------------------------------------------------ */
 
@@ -108,14 +108,14 @@ GdkPixbuf *find_image(const char *name) {
     if((n = TABLE_FIND(images, name, name)) >= 0) {
       /* Use the built-in copy */
       if(!(pb = gdk_pixbuf_new_from_inline(-1, images[n].data, FALSE, &err))) {
-        error(0, "%s", err->message);
+        disorder_error(0, "%s", err->message);
         return 0;
       }
     } else {
       /* See if there's a copy on disk */
       byte_xasprintf(&path, "%s/static/%s", pkgdatadir, name);
       if(!(pb = gdk_pixbuf_new_from_file(path, &err))) {
-        error(0, "%s", err->message);
+        disorder_error(0, "%s", err->message);
         return 0;
       }
     }
@@ -172,7 +172,7 @@ GtkWidget *iconbutton(const char *path, const char *tip) {
   gtk_widget_set_style(content, tool_style);
   gtk_container_add(GTK_CONTAINER(button), content);
   if(tip)
-    gtk_tooltips_set_tip(tips, button, tip, "");
+    gtk_widget_set_tooltip_text(button, tip);
   return button;
 }
 
@@ -187,8 +187,15 @@ GtkWidget *create_buttons_box(struct button *buttons,
     gtk_widget_set_style(buttons[n].widget, tool_style);
     g_signal_connect(G_OBJECT(buttons[n].widget), "clicked",
                      G_CALLBACK(buttons[n].clicked), 0);
-    gtk_box_pack_start(GTK_BOX(box), buttons[n].widget, FALSE, FALSE, 1);
-    gtk_tooltips_set_tip(tips, buttons[n].widget, buttons[n].tip, "");
+    void (*pack)(GtkBox *box,
+                 GtkWidget *child,
+                 gboolean expand,
+                 gboolean fill,
+                 guint padding);
+    if(!(pack = buttons[n].pack))
+      pack = gtk_box_pack_start;
+    pack(GTK_BOX(box), buttons[n].widget, FALSE, FALSE, 1);
+    gtk_widget_set_tooltip_text(buttons[n].widget, buttons[n].tip);
   }
   return box;
 }
diff --git a/disobedience/multidrag.c b/disobedience/multidrag.c
new file mode 100644 (file)
index 0000000..f4a5a76
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * Note that this license ONLY applies to multidrag.c and multidrag.h, not to
+ * the rest of DisOrder.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/** @file disobedience/multidrag.c
+ * @brief Drag multiple rows of a GtkTreeView
+ *
+ * Normally when you start a drag, GtkTreeView sets the selection to just row
+ * you dragged from (because it can't cope with dragging more than one row at a
+ * time).
+ *
+ * Disobedience needs more.
+ *
+ * Firstly it intercepts button-press-event and button-release event and for
+ * clicks that might be the start of drags, suppresses changes to the
+ * selection.  A consequence of this is that it needs to intercept
+ * button-release-event too, to restore the effect of the click, if it turns
+ * out not to be drag after all.
+ *
+ * The location of the initial click is stored in object data called @c
+ * multidrag-where.
+ *
+ * Secondly it intercepts drag-begin and constructs an icon from the rows to be
+ * dragged.
+ *
+ * Inspired by similar code in <a
+ * href="http://code.google.com/p/quodlibet/">Quodlibet</a> (another software
+ * jukebox, albeit as far as I can see a single-user one).
+ */
+#include <config.h>
+
+#include <string.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "multidrag.h"
+
+static gboolean multidrag_selection_block(GtkTreeSelection attribute((unused)) *selection,
+                                         GtkTreeModel attribute((unused)) *model,
+                                         GtkTreePath attribute((unused)) *path,
+                                         gboolean attribute((unused)) path_currently_selected,
+                                         gpointer data) {
+  return *(const gboolean *)data;
+}
+
+static void block_selection(GtkWidget *w, gboolean block,
+                           int x, int y) {
+  static const gboolean which[] = { FALSE, TRUE };
+  GtkTreeSelection *s = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));
+  gtk_tree_selection_set_select_function(s,
+                                        multidrag_selection_block,
+                                        (gboolean *)&which[!!block],
+                                        NULL);
+  // Remember the pointer location
+  int *where = g_object_get_data(G_OBJECT(w), "multidrag-where");
+  if(!where) {
+    where = g_malloc(2 * sizeof (int));
+    g_object_set_data_full(G_OBJECT(w), "multidrag-where", where,
+                           g_free);
+  }
+  where[0] = x;
+  where[1] = y;
+}
+
+static gboolean multidrag_button_press_event(GtkWidget *w,
+                                            GdkEventButton *event,
+                                            gpointer attribute((unused)) user_data) {
+  /* By default we assume that anything this button press does should
+   * act as normal */
+  block_selection(w, TRUE, -1, -1);
+  /* We are only interested in left-button behavior */
+  if(event->button != 1)
+    return FALSE;
+  /* We are only uninterested in clicks without CTRL or SHIFT.  GTK ignores the
+   * other possible modifiers, so we do too. */
+  if(event->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK))
+    return FALSE;
+  /* We are only interested if a well-defined path is clicked */
+  GtkTreePath *path = NULL;
+  if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(w),
+                                   event->x, event->y,
+                                   &path,
+                                   NULL,
+                                   NULL, NULL))
+    return FALSE;
+  /* We are only interested if a selected row is clicked */
+  GtkTreeSelection *s = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));
+  if(gtk_tree_selection_path_is_selected(s, path)) {
+    /* We block subsequent selection changes and remember where the
+     * click was */
+    block_selection(w, FALSE, event->x, event->y);
+  }
+  if(path)
+    gtk_tree_path_free(path);
+  return FALSE;                        /* propagate */
+}
+
+static gboolean multidrag_button_release_event(GtkWidget *w,
+                                              GdkEventButton *event,
+                                              gpointer attribute((unused)) user_data) {
+  int *where = g_object_get_data(G_OBJECT(w), "multidrag-where");
+
+  /* Did button-press-event do anything?  We just check the outcome rather than
+   * going through all the conditions it tests. */
+  if(where && where[0] != -1) {
+    // Remember where the down-click was
+    const int x = where[0], y = where[1];
+    // Re-allow selections
+    block_selection(w, TRUE, -1, -1);
+    if(x == event->x && y == event->y) {
+      // If the up-click is at the same location as the down-click,
+      // it's not a drag.
+      GtkTreePath *path = NULL;
+      GtkTreeViewColumn *col;
+      if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(w),
+                                      event->x, event->y,
+                                      &path,
+                                      &col,
+                                      NULL, NULL)) {
+       gtk_tree_view_set_cursor(GTK_TREE_VIEW(w), path, col, FALSE);
+      }
+      if(path)
+        gtk_tree_path_free(path);
+    }
+  }
+  return FALSE;                        /* propagate */
+}
+
+/** @brief State for multidrag_begin() and its callbacks */
+struct multidrag_begin_state {
+  GtkTreeView *view;
+  multidrag_row_predicate *predicate;
+  int rows;
+  int index;
+  GdkPixmap **pixmaps;
+};
+
+/** @brief Callback to construct a row pixmap */
+static void multidrag_make_row_pixmaps(GtkTreeModel attribute((unused)) *model,
+                                      GtkTreePath *path,
+                                      GtkTreeIter *iter,
+                                      gpointer data) {
+  struct multidrag_begin_state *qdbs = data;
+
+  if(qdbs->predicate(path, iter)) {
+    qdbs->pixmaps[qdbs->index++]
+      = gtk_tree_view_create_row_drag_icon(qdbs->view, path);
+  }
+}
+
+/** @brief Called when a drag operation starts
+ * @param w Source widget (the tree view)
+ * @param dc Drag context
+ * @param user_data Row predicate
+ */
+static void multidrag_drag_begin(GtkWidget *w,
+                                GdkDragContext attribute((unused)) *dc,
+                                gpointer user_data) {
+  struct multidrag_begin_state qdbs[1];
+  GdkPixmap *icon;
+  GtkTreeSelection *sel;
+
+  //fprintf(stderr, "drag-begin\n");
+  memset(qdbs, 0, sizeof *qdbs);
+  qdbs->view = GTK_TREE_VIEW(w);
+  qdbs->predicate = (multidrag_row_predicate *)user_data;
+  sel = gtk_tree_view_get_selection(qdbs->view);
+  /* Find out how many rows there are */
+  if(!(qdbs->rows = gtk_tree_selection_count_selected_rows(sel)))
+    return;                             /* doesn't make sense */
+  /* Generate a pixmap for each row */
+  qdbs->pixmaps = g_new(GdkPixmap *, qdbs->rows);
+  gtk_tree_selection_selected_foreach(sel,
+                                      multidrag_make_row_pixmaps,
+                                      qdbs);
+  /* Might not have used all rows */
+  qdbs->rows = qdbs->index;
+  /* Determine the size of the final icon */
+  int height = 0, width = 0;
+  for(int n = 0; n < qdbs->rows; ++n) {
+    int pxw, pxh;
+    gdk_drawable_get_size(qdbs->pixmaps[n], &pxw, &pxh);
+    if(pxw > width)
+      width = pxw;
+    height += pxh;
+  }
+  if(!width || !height)
+    return;                             /* doesn't make sense */
+  /* Construct the icon */
+  icon = gdk_pixmap_new(qdbs->pixmaps[0], width, height, -1);
+  GdkGC *gc = gdk_gc_new(icon);
+  gdk_gc_set_colormap(gc, gtk_widget_get_colormap(w));
+  int y = 0;
+  for(int n = 0; n < qdbs->rows; ++n) {
+    int pxw, pxh;
+    gdk_drawable_get_size(qdbs->pixmaps[n], &pxw, &pxh);
+    gdk_draw_drawable(icon,
+                      gc,
+                      qdbs->pixmaps[n],
+                      0, 0,             /* source coords */
+                      0, y,             /* dest coords */
+                      pxw, pxh);        /* size */
+    y += pxh;
+    gdk_drawable_unref(qdbs->pixmaps[n]);
+    qdbs->pixmaps[n] = NULL;
+  }
+  g_free(qdbs->pixmaps);
+  qdbs->pixmaps = NULL;
+  // TODO scale down a bit, the resulting icons are currently a bit on the
+  // large side.
+  gtk_drag_source_set_icon(w,
+                           gtk_widget_get_colormap(w),
+                           icon,
+                           NULL);
+}
+
+static gboolean multidrag_default_predicate(GtkTreePath attribute((unused)) *path,
+                                           GtkTreeIter attribute((unused)) *iter) {
+  return TRUE;
+}
+
+/** @brief Allow multi-row drag for @p w
+ * @param w A GtkTreeView widget
+ * @param predicate Function called to test rows for draggability, or NULL
+ *
+ * Suppresses the restriction of selections when a drag is started, and
+ * intercepts drag-begin to construct an icon.
+ *
+ * @p predicate should return TRUE for draggable rows and FALSE otherwise, to
+ * control what goes in the icon.  If NULL, equivalent to a function that
+ * always returns TRUE.
+ */
+void make_treeview_multidrag(GtkWidget *w,
+                            multidrag_row_predicate *predicate) {
+  if(!predicate)
+    predicate = multidrag_default_predicate;
+  g_signal_connect(w, "button-press-event",
+                  G_CALLBACK(multidrag_button_press_event), NULL);
+  g_signal_connect(w, "button-release-event",
+                  G_CALLBACK(multidrag_button_release_event), NULL);
+  g_signal_connect(w, "drag-begin",
+                   G_CALLBACK(multidrag_drag_begin), predicate);
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/disobedience/multidrag.h b/disobedience/multidrag.h
new file mode 100644 (file)
index 0000000..e333046
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * Note that this license ONLY applies to multidrag.c and multidrag.h, not to
+ * the rest of DisOrder.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/** @file disobedience/multidrag.h
+ * @brief Drag multiple rows of a GtkTreeView
+ */
+#ifndef MULTIDRAG_H
+#define MULTIDRAG_H
+
+/** @brief Predicate type for rows to drag
+ * @param path Path to row
+ * @param iter Iterator pointing at row
+ * @return TRUE if row is draggable else FALSE
+ */
+typedef gboolean multidrag_row_predicate(GtkTreePath *path,
+                                        GtkTreeIter *iter);
+
+void make_treeview_multidrag(GtkWidget *w,
+                            multidrag_row_predicate *predicate);
+
+#endif /* MULTIDRAG_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/disobedience/playlists.c b/disobedience/playlists.c
new file mode 100644 (file)
index 0000000..4d4194e
--- /dev/null
@@ -0,0 +1,1635 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2008, 2009 Richard Kettlewell
+ *
+ * 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 program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+/** @file disobedience/playlists.c
+ * @brief Playlist support for Disobedience
+ *
+ * The playlists management window contains:
+ * - the playlist picker (a list of all playlists) TODO should be a tree!
+ * - an add button
+ * - a delete button
+ * - the playlist editor (a d+d-capable view of the currently picked playlist)
+ * - a close button   TODO
+ *
+ * This file also maintains the playlist menu, allowing playlists to be
+ * activated from the main window's menu.
+ *
+ * Internally we maintain the playlist list, which is just the current list of
+ * playlists.  Changes to this are reflected in the playlist menu and the
+ * playlist picker.
+ *
+ */
+#include "disobedience.h"
+#include "queue-generic.h"
+#include "popup.h"
+#include "validity.h"
+
+static void playlist_list_received_playlists(void *v,
+                                             const char *err,
+                                             int nvec, char **vec);
+static void playlist_editor_fill(const char *event,
+                                 void *eventdata,
+                                 void *callbackdata);
+static int playlist_playall_sensitive(void *extra);
+static void playlist_playall_activate(GtkMenuItem *menuitem,
+                                      gpointer user_data);
+static int playlist_remove_sensitive(void *extra) ;
+static void playlist_remove_activate(GtkMenuItem *menuitem,
+                                     gpointer user_data);
+static void playlist_new_locked(void *v, const char *err);
+static void playlist_new_retrieved(void *v, const char *err,
+                                   int nvec,
+                                   char **vec);
+static void playlist_new_created(void *v, const char *err);
+static void playlist_new_unlocked(void *v, const char *err);
+static void playlist_new_entry_edited(GtkEditable *editable,
+                                      gpointer user_data);
+static void playlist_new_button_toggled(GtkToggleButton *tb,
+                                        gpointer userdata);
+static void playlist_new_changed(const char *event,
+                                 void *eventdata,
+                                 void *callbackdata);
+static const char *playlist_new_valid(void);
+static void playlist_new_details(char **namep,
+                                 char **fullnamep,
+                                 gboolean *sharedp,
+                                 gboolean *publicp,
+                                 gboolean *privatep);
+static void playlist_new_ok(GtkButton *button,
+                            gpointer userdata);
+static void playlist_new_cancel(GtkButton *button,
+                                gpointer userdata);
+static void playlists_editor_received_tracks(void *v,
+                                             const char *err,
+                                             int nvec, char **vec);
+static void playlist_window_destroyed(GtkWidget *widget,
+                                      GtkWidget **widget_pointer);
+static gboolean playlist_window_keypress(GtkWidget *widget,
+                                         GdkEventKey *event,
+                                         gpointer user_data);
+static int playlistcmp(const void *ap, const void *bp);
+static void playlist_modify_locked(void *v, const char *err);
+void playlist_modify_retrieved(void *v, const char *err,
+                               int nvec,
+                               char **vec);
+static void playlist_modify_updated(void *v, const char *err);
+static void playlist_modify_unlocked(void *v, const char *err);
+static void playlist_drop(struct queuelike *ql,
+                          int ntracks,
+                          char **tracks, char **ids,
+                          struct queue_entry *after_me);
+struct playlist_modify_data;
+static void playlist_drop_modify(struct playlist_modify_data *mod,
+                                 int nvec, char **vec);
+static void playlist_remove_modify(struct playlist_modify_data *mod,
+                                 int nvec, char **vec);
+static gboolean playlist_new_keypress(GtkWidget *widget,
+                                      GdkEventKey *event,
+                                      gpointer user_data);
+static gboolean playlist_picker_keypress(GtkWidget *widget,
+                                         GdkEventKey *event,
+                                         gpointer user_data);
+static void playlist_editor_button_toggled(GtkToggleButton *tb,
+                                           gpointer userdata);
+static void playlist_editor_set_buttons(const char *event,
+                                        void *eventdata,
+                                        void *callbackdata);
+static void playlist_editor_got_share(void *v,
+                                      const char *err,
+                                      const char *value);
+static void playlist_editor_share_set(void *v, const char *err);
+static void playlist_picker_update_section(const char *title, const char *key,
+                                           int start, int end);
+static gboolean playlist_picker_find(GtkTreeIter *parent,
+                                     const char *title, const char *key,
+                                     GtkTreeIter iter[1],
+                                     gboolean create);
+static void playlist_picker_delete_obsolete(GtkTreeIter parent[1],
+                                            char **exists,
+                                            int nexists);
+static gboolean playlist_picker_button(GtkWidget *widget,
+                                       GdkEventButton *event,
+                                       gpointer user_data);
+static gboolean playlist_editor_keypress(GtkWidget *widget,
+                                         GdkEventKey *event,
+                                         gpointer user_data);
+static void playlist_editor_ok(GtkButton *button, gpointer userdata);
+static void playlist_editor_help(GtkButton *button, gpointer userdata);
+
+/** @brief Playlist editing window */
+static GtkWidget *playlist_window;
+
+/** @brief Columns for the playlist editor */
+static const struct queue_column playlist_columns[] = {
+  { "Artist", column_namepart, "artist", COL_EXPAND|COL_ELLIPSIZE },
+  { "Album",  column_namepart, "album",  COL_EXPAND|COL_ELLIPSIZE },
+  { "Title",  column_namepart, "title",  COL_EXPAND|COL_ELLIPSIZE },
+};
+
+/** @brief Pop-up menu for playlist editor
+ *
+ * Status:
+ * - track properties works but, bizarrely, raises the main window
+ * - play track works
+ * - play playlist works
+ * - select/deselect all work
+ */
+static struct menuitem playlist_menuitems[] = {
+  { "Track properties", GTK_STOCK_PROPERTIES, ql_properties_activate, ql_properties_sensitive, 0, 0 },
+  { "Play track", GTK_STOCK_MEDIA_PLAY, ql_play_activate, ql_play_sensitive, 0, 0 },
+  { "Play playlist", NULL, playlist_playall_activate, playlist_playall_sensitive, 0, 0 },
+  { "Remove track from playlist", GTK_STOCK_DELETE, playlist_remove_activate, playlist_remove_sensitive, 0, 0 },
+  { "Select all tracks", GTK_STOCK_SELECT_ALL, ql_selectall_activate, ql_selectall_sensitive, 0, 0 },
+  { "Deselect all tracks", NULL, ql_selectnone_activate, ql_selectnone_sensitive, 0, 0 },
+};
+
+static const GtkTargetEntry playlist_targets[] = {
+  {
+    PLAYLIST_TRACKS,                    /* drag type */
+    GTK_TARGET_SAME_WIDGET,             /* rearrangement within a widget */
+    PLAYLIST_TRACKS_ID                  /* ID value */
+  },
+  {
+    PLAYABLE_TRACKS,                             /* drag type */
+    GTK_TARGET_SAME_APP|GTK_TARGET_OTHER_WIDGET, /* copying between widgets */
+    PLAYABLE_TRACKS_ID,                          /* ID value */
+  },
+  {
+    .target = NULL
+  }
+};
+
+/** @brief Queuelike for editing a playlist */
+static struct queuelike ql_playlist = {
+  .name = "playlist",
+  .columns = playlist_columns,
+  .ncolumns = sizeof playlist_columns / sizeof *playlist_columns,
+  .menuitems = playlist_menuitems,
+  .nmenuitems = sizeof playlist_menuitems / sizeof *playlist_menuitems,
+  .drop = playlist_drop,
+  .drag_source_targets = playlist_targets,
+  .drag_source_actions = GDK_ACTION_MOVE|GDK_ACTION_COPY,
+  .drag_dest_targets = playlist_targets,
+  .drag_dest_actions = GDK_ACTION_MOVE|GDK_ACTION_COPY,
+};
+
+/* Maintaining the list of playlists ---------------------------------------- */
+
+/** @brief Current list of playlists or NULL */
+char **playlists;
+
+/** @brief Count of playlists */
+int nplaylists;
+
+/** @brief Schedule an update to the list of playlists
+ *
+ * Called periodically and when a playlist is created or deleted.
+ */
+static void playlist_list_update(const char attribute((unused)) *event,
+                                 void attribute((unused)) *eventdata,
+                                 void attribute((unused)) *callbackdata) {
+  disorder_eclient_playlists(client, playlist_list_received_playlists, 0);
+}
+
+/** @brief Called with a new list of playlists */
+static void playlist_list_received_playlists(void attribute((unused)) *v,
+                                             const char *err,
+                                             int nvec, char **vec) {
+  if(err) {
+    playlists = 0;
+    nplaylists = -1;
+    /* Probably means server does not support playlists */
+  } else {
+    playlists = vec;
+    nplaylists = nvec;
+    qsort(playlists, nplaylists, sizeof (char *), playlistcmp);
+  }
+  /* Tell our consumers */
+  event_raise("playlists-updated", 0);
+}
+
+/** @brief qsort() callback for playlist name comparison */
+static int playlistcmp(const void *ap, const void *bp) {
+  const char *a = *(char **)ap, *b = *(char **)bp;
+  const char *ad = strchr(a, '.'), *bd = strchr(b, '.');
+  int c;
+
+  /* Group owned playlists by owner */
+  if(ad && bd) {
+    const int adn = ad - a, bdn = bd - b;
+    if((c = strncmp(a, b, adn < bdn ? adn : bdn)))
+      return c;
+    /* Lexical order within playlists of a single owner */
+    return strcmp(ad + 1, bd + 1);
+  }
+
+  /* Owned playlists after shared ones */
+  if(ad) {
+    return 1;
+  } else if(bd) {
+    return -1;
+  }
+
+  /* Lexical order of shared playlists */
+  return strcmp(a, b);
+}
+
+/* Playlists menu ----------------------------------------------------------- */
+
+static void playlist_menu_playing(void attribute((unused)) *v,
+                                  const char *err) {
+  if(err)
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+}
+
+/** @brief Play received playlist contents
+ *
+ * Passed as a completion callback by menu_activate_playlist().
+ */
+static void playlist_menu_received_content(void attribute((unused)) *v,
+                                           const char *err,
+                                           int nvec, char **vec) {
+  if(err) {
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+    return;
+  }
+  for(int n = 0; n < nvec; ++n)
+    disorder_eclient_play(client, vec[n], playlist_menu_playing, NULL);
+}
+
+/** @brief Called to activate a playlist
+ *
+ * Called when the menu item for a playlist is clicked.
+ */
+static void playlist_menu_activate(GtkMenuItem *menuitem,
+                                   gpointer attribute((unused)) user_data) {
+  GtkLabel *label = GTK_LABEL(GTK_BIN(menuitem)->child);
+  const char *playlist = gtk_label_get_text(label);
+
+  disorder_eclient_playlist_get(client, playlist_menu_received_content,
+                                playlist, NULL);
+}
+
+/** @brief Called when the playlists change
+ *
+ * Naively refills the menu.  The results might be unsettling if the menu is
+ * currently open, but this is hopefuly fairly rare.
+ */
+static void playlist_menu_changed(const char attribute((unused)) *event,
+                                  void attribute((unused)) *eventdata,
+                                  void attribute((unused)) *callbackdata) {
+  if(!playlists_menu)
+    return;                             /* OMG too soon */
+  GtkMenuShell *menu = GTK_MENU_SHELL(playlists_menu);
+  while(menu->children)
+    gtk_container_remove(GTK_CONTAINER(menu), GTK_WIDGET(menu->children->data));
+  /* NB nplaylists can be -1 as well as 0 */
+  for(int n = 0; n < nplaylists; ++n) {
+    GtkWidget *w = gtk_menu_item_new_with_label(playlists[n]);
+    g_signal_connect(w, "activate", G_CALLBACK(playlist_menu_activate), 0);
+    gtk_widget_show(w);
+    gtk_menu_shell_append(menu, w);
+  }
+  gtk_widget_set_sensitive(menu_playlists_widget,
+                           nplaylists > 0);
+  gtk_widget_set_sensitive(menu_editplaylists_widget,
+                           nplaylists >= 0);
+}
+
+/* Popup to create a new playlist ------------------------------------------- */
+
+/** @brief New-playlist popup */
+static GtkWidget *playlist_new_window;
+
+/** @brief Text entry in new-playlist popup */
+static GtkWidget *playlist_new_entry;
+
+/** @brief Label for displaying feedback on what's wrong */
+static GtkWidget *playlist_new_info;
+
+/** @brief "Shared" radio button */
+static GtkWidget *playlist_new_shared;
+
+/** @brief "Public" radio button */
+static GtkWidget *playlist_new_public;
+
+/** @brief "Private" radio button */
+static GtkWidget *playlist_new_private;
+
+/** @brief Buttons for new-playlist popup */
+static struct button playlist_new_buttons[] = {
+  {
+    .stock = GTK_STOCK_OK,
+    .clicked = playlist_new_ok,
+    .tip = "Create new playlist"
+  },
+  {
+    .stock = GTK_STOCK_CANCEL,
+    .clicked = playlist_new_cancel,
+    .tip = "Do not create new playlist"
+  }
+};
+#define NPLAYLIST_NEW_BUTTONS (sizeof playlist_new_buttons / sizeof *playlist_new_buttons)
+
+/** @brief Pop up a new window to enter the playlist name and details */
+static void playlist_new_playlist(void) {
+  assert(playlist_new_window == NULL);
+  playlist_new_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  g_signal_connect(playlist_new_window, "destroy",
+                  G_CALLBACK(gtk_widget_destroyed), &playlist_new_window);
+  gtk_window_set_title(GTK_WINDOW(playlist_new_window), "Create new playlist");
+  /* Window will be modal, suppressing access to other windows */
+  gtk_window_set_modal(GTK_WINDOW(playlist_new_window), TRUE);
+  gtk_window_set_transient_for(GTK_WINDOW(playlist_new_window),
+                               GTK_WINDOW(playlist_window));
+
+  /* Window contents will use a table (grid) layout */
+  GtkWidget *table = gtk_table_new(3, 3, FALSE/*!homogeneous*/);
+
+  /* First row: playlist name */
+  gtk_table_attach_defaults(GTK_TABLE(table),
+                            gtk_label_new("Playlist name"),
+                            0, 1, 0, 1);
+  playlist_new_entry = gtk_entry_new();
+  g_signal_connect(playlist_new_entry, "changed",
+                   G_CALLBACK(playlist_new_entry_edited), NULL);
+  gtk_table_attach_defaults(GTK_TABLE(table),
+                            playlist_new_entry,
+                            1, 3, 0, 1);
+
+  /* Second row: radio buttons to choose type */
+  playlist_new_shared = gtk_radio_button_new_with_label(NULL, "shared");
+  playlist_new_public
+    = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(playlist_new_shared),
+                                                  "public");
+  playlist_new_private
+    = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(playlist_new_shared),
+                                                  "private");
+  g_signal_connect(playlist_new_shared, "toggled",
+                   G_CALLBACK(playlist_new_button_toggled), NULL);
+  g_signal_connect(playlist_new_public, "toggled",
+                   G_CALLBACK(playlist_new_button_toggled), NULL);
+  g_signal_connect(playlist_new_private, "toggled",
+                   G_CALLBACK(playlist_new_button_toggled), NULL);
+  gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_shared, 0, 1, 1, 2);
+  gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_public, 1, 2, 1, 2);
+  gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_private, 2, 3, 1, 2);
+
+  /* Third row: info bar saying why not */
+  playlist_new_info = gtk_label_new("");
+  gtk_table_attach_defaults(GTK_TABLE(table), playlist_new_info,
+                            0, 3, 2, 3);
+
+  /* Fourth row: ok/cancel buttons */
+  GtkWidget *hbox = create_buttons_box(playlist_new_buttons,
+                                       NPLAYLIST_NEW_BUTTONS,
+                                       gtk_hbox_new(FALSE, 0));
+  gtk_table_attach_defaults(GTK_TABLE(table), hbox, 0, 3, 3, 4);
+
+  gtk_container_add(GTK_CONTAINER(playlist_new_window),
+                    frame_widget(table, NULL));
+
+  /* Set initial state of OK button */
+  playlist_new_changed(0,0,0);
+
+  g_signal_connect(playlist_new_window, "key-press-event",
+                   G_CALLBACK(playlist_new_keypress), 0);
+  
+  /* Display the window */
+  gtk_widget_show_all(playlist_new_window);
+}
+
+/** @brief Keypress handler */
+static gboolean playlist_new_keypress(GtkWidget attribute((unused)) *widget,
+                                      GdkEventKey *event,
+                                      gpointer attribute((unused)) user_data) {
+  if(event->state)
+    return FALSE;
+  switch(event->keyval) {
+  case GDK_Return:
+    playlist_new_ok(NULL, NULL);
+    return TRUE;
+  case GDK_Escape:
+    gtk_widget_destroy(playlist_new_window);
+    return TRUE;
+  default:
+    return FALSE;
+  }
+}
+
+/** @brief Called when 'ok' is clicked in new-playlist popup */
+static void playlist_new_ok(GtkButton attribute((unused)) *button,
+                            gpointer attribute((unused)) userdata) {
+  if(playlist_new_valid())
+    return;
+  gboolean shared, public, private;
+  char *name, *fullname;
+  playlist_new_details(&name, &fullname, &shared, &public, &private);
+
+  /* We need to:
+   * - lock the playlist
+   * - check it doesn't exist
+   * - set sharing (which will create it empty
+   * - unlock it
+   *
+   * TODO we should freeze the window while this is going on to stop a second
+   * click.
+   */
+  disorder_eclient_playlist_lock(client, playlist_new_locked, fullname,
+                                 fullname);
+}
+
+/** @brief Called when the proposed new playlist has been locked */
+static void playlist_new_locked(void *v, const char *err) {
+  char *fullname = v;
+  if(err) {
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+    return;
+  }
+  disorder_eclient_playlist_get(client, playlist_new_retrieved,
+                                fullname, fullname);
+}
+
+/** @brief Called when the proposed new playlist's contents have been retrieved
+ *
+ * ...or rather, normally, when it's been reported that it does not exist.
+ */
+static void playlist_new_retrieved(void *v, const char *err,
+                                   int nvec,
+                                   char attribute((unused)) **vec) {
+  char *fullname = v;
+  if(!err && nvec != -1)
+    /* A rare case but not in principle impossible */
+    err = "A playlist with that name already exists.";
+  if(err) {
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+    disorder_eclient_playlist_unlock(client, playlist_new_unlocked, fullname);
+    return;
+  }
+  gboolean shared, public, private;
+  playlist_new_details(0, 0, &shared, &public, &private);
+  disorder_eclient_playlist_set_share(client, playlist_new_created, fullname,
+                                      public ? "public"
+                                      : private ? "private"
+                                      : "shared",
+                                      fullname);
+}
+
+/** @brief Called when the new playlist has been created */
+static void playlist_new_created(void attribute((unused)) *v, const char *err) {
+  if(err) {
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+    return;
+  }
+  disorder_eclient_playlist_unlock(client, playlist_new_unlocked, NULL);
+  // TODO arrange for the new playlist to be selected
+}
+
+/** @brief Called when the newly created playlist has unlocked */
+static void playlist_new_unlocked(void attribute((unused)) *v, const char *err) {
+  if(err)
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+  /* Pop down the creation window */
+  gtk_widget_destroy(playlist_new_window);
+}
+
+/** @brief Called when 'cancel' is clicked in new-playlist popup */
+static void playlist_new_cancel(GtkButton attribute((unused)) *button,
+                                gpointer attribute((unused)) userdata) {
+  gtk_widget_destroy(playlist_new_window);
+}
+
+/** @brief Called when some radio button in the new-playlist popup changes */
+static void playlist_new_button_toggled(GtkToggleButton attribute((unused)) *tb,
+                                        gpointer attribute((unused)) userdata) {
+  playlist_new_changed(0,0,0);
+}
+
+/** @brief Called when the text entry field in the new-playlist popup changes */
+static void playlist_new_entry_edited(GtkEditable attribute((unused)) *editable,
+                                      gpointer attribute((unused)) user_data) {
+  playlist_new_changed(0,0,0);
+}
+
+/** @brief Called to update new playlist window state
+ *
+ * This is called whenever one the text entry or radio buttons changed, and
+ * also when the set of known playlists changes.  It determines whether the new
+ * playlist would be creatable and sets the sensitivity of the OK button
+ * and info display accordingly.
+ */
+static void playlist_new_changed(const char attribute((unused)) *event,
+                                 void attribute((unused)) *eventdata,
+                                 void attribute((unused)) *callbackdata) {
+  if(!playlist_new_window)
+    return;
+  const char *reason = playlist_new_valid();
+  gtk_widget_set_sensitive(playlist_new_buttons[0].widget,
+                           !reason);
+  gtk_label_set_text(GTK_LABEL(playlist_new_info), reason);
+}
+
+/** @brief Test whether the new-playlist window settings are valid
+ * @return NULL on success or an error string if not
+ */
+static const char *playlist_new_valid(void) {
+  gboolean shared, public, private;
+  char *name, *fullname;
+  playlist_new_details(&name, &fullname, &shared, &public, &private);
+  if(!(shared || public || private))
+    return "No type set.";
+  if(!*name)
+    return "";
+  /* See if the result is valid */
+  if(!valid_username(name)
+     || playlist_parse_name(fullname, NULL, NULL))
+    return "Not a valid playlist name.";
+  /* See if the result clashes with an existing name.  This is not a perfect
+   * check, the playlist might be created after this point but before we get a
+   * chance to disable the "OK" button.  However when we try to create the
+   * playlist we will first try to retrieve it, with a lock held, so we
+   * shouldn't end up overwriting anything. */
+  for(int n = 0; n < nplaylists; ++n)
+    if(!strcmp(playlists[n], fullname)) {
+      if(shared)
+        return "A shared playlist with that name already exists.";
+      else
+        return "You already have a playlist with that name.";
+    }
+  /* As far as we can tell creation would work */
+  return NULL;
+}
+
+/** @brief Get entered new-playlist details
+ * @param namep Where to store entered name (or NULL)
+ * @param fullnamep Where to store computed full name (or NULL)
+ * @param sharep Where to store 'shared' flag (or NULL)
+ * @param publicp Where to store 'public' flag (or NULL)
+ * @param privatep Where to store 'private' flag (or NULL)
+ */
+static void playlist_new_details(char **namep,
+                                 char **fullnamep,
+                                 gboolean *sharedp,
+                                 gboolean *publicp,
+                                 gboolean *privatep) {
+  gboolean shared, public, private;
+  g_object_get(playlist_new_shared, "active", &shared, (char *)NULL);
+  g_object_get(playlist_new_public, "active", &public, (char *)NULL);
+  g_object_get(playlist_new_private, "active", &private, (char *)NULL);
+  char *gname = gtk_editable_get_chars(GTK_EDITABLE(playlist_new_entry),
+                                       0, -1); /* name owned by calle */
+  char *name = xstrdup(gname);
+  g_free(gname);
+  if(sharedp) *sharedp = shared;
+  if(publicp) *publicp = public;
+  if(privatep) *privatep = private;
+  if(namep) *namep = name;
+  if(fullnamep) {
+    if(*sharedp) *fullnamep = *namep;
+    else byte_xasprintf(fullnamep, "%s.%s", config->username, name);
+  }
+}
+
+/* Playlist picker ---------------------------------------------------------- */
+
+/** @brief Delete button */
+static GtkWidget *playlist_picker_delete_button;
+
+/** @brief Tree model for list of playlists
+ *
+ * This has two columns:
+ * - column 0 will be the display name
+ * - column 1 will be the sort key/playlist name (and will not be displayed)
+ */
+static GtkTreeStore *playlist_picker_list;
+
+/** @brief Selection for list of playlists */
+static GtkTreeSelection *playlist_picker_selection;
+
+/** @brief Currently selected playlist */
+static const char *playlist_picker_selected;
+
+/** @brief (Re-)populate the playlist picker tree model */
+static void playlist_picker_fill(const char attribute((unused)) *event,
+                                 void attribute((unused)) *eventdata,
+                                 void attribute((unused)) *callbackdata) {
+  if(!playlist_window)
+    return;
+  if(!playlist_picker_list)
+    playlist_picker_list = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+  /* We will accumulate a list of all the sections that exist */
+  char **sections = xcalloc(nplaylists, sizeof (char *));
+  int nsections = 0;
+  /* Make sure shared playlists are there */
+  int start = 0, end;
+  for(end = start; end < nplaylists && !strchr(playlists[end], '.'); ++end)
+    ;
+  if(start != end) {
+    playlist_picker_update_section("Shared playlists", "",
+                                   start, end);
+    sections[nsections++] = (char *)"";
+  }
+  /* Make sure owned playlists are there */
+  while((start = end) < nplaylists) {
+    const int nl = strchr(playlists[start], '.') - playlists[start];
+    char *name = xstrndup(playlists[start], nl);
+    for(end = start;
+        end < nplaylists
+          && playlists[end][nl] == '.'
+          && !strncmp(playlists[start], playlists[end], nl);
+        ++end)
+      ;
+    playlist_picker_update_section(name, name, start, end);
+    sections[nsections++] = name;
+  }
+  /* Delete obsolete sections */
+  playlist_picker_delete_obsolete(NULL, sections, nsections);
+}
+
+/** @brief Update a section in the picker tree model
+ * @param section Section name
+ * @param start First entry in @ref playlists
+ * @param end Past last entry in @ref playlists
+ */
+static void playlist_picker_update_section(const char *title, const char *key,
+                                           int start, int end) {
+  /* Find the section, creating it if necessary */
+  GtkTreeIter section_iter[1];
+  playlist_picker_find(NULL, title, key, section_iter, TRUE);
+  /* Add missing rows */
+  for(int n = start; n < end; ++n) {
+    GtkTreeIter child[1];
+    char *name;
+    if((name = strchr(playlists[n], '.')))
+      ++name;
+    else
+      name = playlists[n];
+    playlist_picker_find(section_iter,
+                         name, playlists[n],
+                         child,
+                         TRUE);
+  }
+  /* Delete anything that shouldn't exist. */
+  playlist_picker_delete_obsolete(section_iter, playlists + start, end - start);
+}
+
+/** @brief Find and maybe create a row in the picker tree model
+ * @param parent Parent iterator (or NULL for top level)
+ * @param title Display name of section
+ * @param key Key to search for
+ * @param iter Iterator to point at key
+ * @param create If TRUE, key will be created if it doesn't exist
+ * @param compare Row comparison function
+ * @return TRUE if key exists else FALSE
+ *
+ * If the @p key exists then @p iter will point to it and TRUE will be
+ * returned.
+ *
+ * If the @p key does not exist and @p create is TRUE then it will be created.
+ * @p iter wil point to it and TRUE will be returned.
+ *
+ * If the @p key does not exist and @p create is FALSE then FALSE will be
+ * returned.
+ */
+static gboolean playlist_picker_find(GtkTreeIter *parent,
+                                     const char *title,
+                                     const char *key,
+                                     GtkTreeIter iter[1],
+                                     gboolean create) {
+  gchar *candidate;
+  GtkTreeIter next[1];
+  gboolean it;
+  int row = 0;
+
+  it = gtk_tree_model_iter_children(GTK_TREE_MODEL(playlist_picker_list),
+                                    next,
+                                    parent);
+  while(it) {
+    /* Find the value at row 'next' */
+    gtk_tree_model_get(GTK_TREE_MODEL(playlist_picker_list),
+                       next,
+                       1, &candidate,
+                       -1);
+    /* See how it compares with @p key */
+    int c = strcmp(key, candidate);
+    g_free(candidate);
+    if(!c) {
+      *iter = *next;
+      return TRUE;                      /* we found our key */
+    }
+    if(c < 0) {
+      /* @p key belongs before row 'next' */
+      if(create) {
+        gtk_tree_store_insert_with_values(playlist_picker_list,
+                                          iter,
+                                          parent,
+                                          row,     /* insert here */
+                                          0, title, 1, key, -1);
+        return TRUE;
+      } else
+        return FALSE;
+      ++row;
+    }
+    it = gtk_tree_model_iter_next(GTK_TREE_MODEL(playlist_picker_list), next);
+  }
+  /* We have reached the end and not found a row that should be later than @p
+   * key. */
+  if(create) {
+    gtk_tree_store_insert_with_values(playlist_picker_list,
+                                      iter,
+                                      parent,
+                                      INT_MAX, /* insert at end */
+                                      0, title, 1, key, -1);
+    return TRUE;
+  } else
+    return FALSE;
+}
+
+/** @brief Delete obsolete rows
+ * @param parent Parent or NULL
+ * @param exists List of rows that should exist (by key)
+ * @param nexists Length of @p exists
+ */
+static void playlist_picker_delete_obsolete(GtkTreeIter parent[1],
+                                            char **exists,
+                                            int nexists) {
+  /* Delete anything that shouldn't exist. */
+  GtkTreeIter iter[1];
+  gboolean it = gtk_tree_model_iter_children(GTK_TREE_MODEL(playlist_picker_list),
+                                             iter,
+                                             parent);
+  while(it) {
+    /* Find the value at row 'next' */
+    gchar *candidate;
+    gtk_tree_model_get(GTK_TREE_MODEL(playlist_picker_list),
+                       iter,
+                       1, &candidate,
+                       -1);
+    gboolean found = FALSE;
+    for(int n = 0; n < nexists; ++n)
+      if((found = !strcmp(candidate, exists[n])))
+        break;
+    if(!found)
+      it = gtk_tree_store_remove(playlist_picker_list, iter);
+    else
+      it = gtk_tree_model_iter_next(GTK_TREE_MODEL(playlist_picker_list),
+                                    iter);
+    g_free(candidate);
+  }
+}
+
+/** @brief Called when the selection might have changed */
+static void playlist_picker_selection_changed(GtkTreeSelection attribute((unused)) *treeselection,
+                                              gpointer attribute((unused)) user_data) {
+  GtkTreeIter iter;
+  char *gselected, *selected;
+  
+  /* Identify the current selection */
+  if(gtk_tree_selection_get_selected(playlist_picker_selection, 0, &iter)
+     && gtk_tree_store_iter_depth(playlist_picker_list, &iter) > 0) {
+    gtk_tree_model_get(GTK_TREE_MODEL(playlist_picker_list), &iter,
+                       1, &gselected, -1);
+    selected = xstrdup(gselected);
+    g_free(gselected);
+  } else
+    selected = 0;
+  /* Set button sensitivity according to the new state */
+  int deletable = FALSE;
+  if(selected) {
+    if(strchr(selected, '.')) {
+      if(!strncmp(selected, config->username, strlen(config->username)))
+        deletable = TRUE;
+    } else
+      deletable = TRUE;
+  }
+  gtk_widget_set_sensitive(playlist_picker_delete_button, deletable);
+  /* Eliminate no-change cases */
+  if(!selected && !playlist_picker_selected)
+    return;
+  if(selected
+     && playlist_picker_selected
+     && !strcmp(selected, playlist_picker_selected))
+    return;
+  /* Record the new state */
+  playlist_picker_selected = selected;
+  /* Re-initalize the queue */
+  ql_new_queue(&ql_playlist, NULL);
+  /* Synthesize a playlist-modified to re-initialize the editor etc */
+  event_raise("playlist-modified", (void *)playlist_picker_selected);
+}
+
+/** @brief Called when the 'add' button is pressed */
+static void playlist_picker_add(GtkButton attribute((unused)) *button,
+                                gpointer attribute((unused)) userdata) {
+  /* Unselect whatever is selected TODO why?? */
+  gtk_tree_selection_unselect_all(playlist_picker_selection);
+  playlist_new_playlist();
+}
+
+/** @brief Called when playlist deletion completes */
+static void playlists_picker_delete_completed(void attribute((unused)) *v,
+                                              const char *err) {
+  if(err)
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+}
+
+/** @brief Called when the 'Delete' button is pressed */
+static void playlist_picker_delete(GtkButton attribute((unused)) *button,
+                                   gpointer attribute((unused)) userdata) {
+  GtkWidget *yesno;
+  int res;
+
+  if(!playlist_picker_selected)
+    return;
+  yesno = gtk_message_dialog_new(GTK_WINDOW(playlist_window),
+                                 GTK_DIALOG_MODAL,
+                                 GTK_MESSAGE_QUESTION,
+                                 GTK_BUTTONS_YES_NO,
+                                 "Do you really want to delete playlist %s?"
+                                 " This action cannot be undone.",
+                                 playlist_picker_selected);
+  res = gtk_dialog_run(GTK_DIALOG(yesno));
+  gtk_widget_destroy(yesno);
+  if(res == GTK_RESPONSE_YES) {
+    disorder_eclient_playlist_delete(client,
+                                     playlists_picker_delete_completed,
+                                     playlist_picker_selected,
+                                     NULL);
+  }
+}
+
+/** @brief Table of buttons below the playlist list */
+static struct button playlist_picker_buttons[] = {
+  {
+    GTK_STOCK_ADD,
+    playlist_picker_add,
+    "Create a new playlist",
+    0,
+    NULL,
+  },
+  {
+    GTK_STOCK_REMOVE,
+    playlist_picker_delete,
+    "Delete a playlist",
+    0,
+    NULL,
+  },
+};
+#define NPLAYLIST_PICKER_BUTTONS (sizeof playlist_picker_buttons / sizeof *playlist_picker_buttons)
+
+/** @brief Create the list of playlists for the edit playlists window */
+static GtkWidget *playlist_picker_create(void) {
+  /* Create the list of playlist and populate it */
+  playlist_picker_fill(NULL, NULL, NULL);
+  /* Create the tree view */
+  GtkWidget *tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(playlist_picker_list));
+  /* ...and the renderers for it */
+  GtkCellRenderer *cr = gtk_cell_renderer_text_new();
+  GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes("Playlist",
+                                                                    cr,
+                                                                    "text", 0,
+                                                                    NULL);
+  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), col);
+  /* Get the selection for the view; set its mode; arrange for a callback when
+   * it changes */
+  playlist_picker_selected = NULL;
+  playlist_picker_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+  gtk_tree_selection_set_mode(playlist_picker_selection, GTK_SELECTION_BROWSE);
+  g_signal_connect(playlist_picker_selection, "changed",
+                   G_CALLBACK(playlist_picker_selection_changed), NULL);
+
+  /* Create the control buttons */
+  GtkWidget *buttons = create_buttons_box(playlist_picker_buttons,
+                                          NPLAYLIST_PICKER_BUTTONS,
+                                          gtk_hbox_new(FALSE, 1));
+  playlist_picker_delete_button = playlist_picker_buttons[1].widget;
+
+  playlist_picker_selection_changed(NULL, NULL);
+
+  /* Buttons live below the list */
+  GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox), scroll_widget(tree), TRUE/*expand*/, TRUE/*fill*/, 0);
+  gtk_box_pack_start(GTK_BOX(vbox), buttons, FALSE/*expand*/, FALSE, 0);
+
+  g_signal_connect(tree, "key-press-event",
+                   G_CALLBACK(playlist_picker_keypress), 0);
+  g_signal_connect(tree, "button-press-event",
+                   G_CALLBACK(playlist_picker_button), 0);
+
+  return vbox;
+}
+
+static gboolean playlist_picker_keypress(GtkWidget attribute((unused)) *widget,
+                                         GdkEventKey *event,
+                                         gpointer attribute((unused)) user_data) {
+  if(event->state)
+    return FALSE;
+  switch(event->keyval) {
+  case GDK_BackSpace:
+  case GDK_Delete:
+    playlist_picker_delete(NULL, NULL);
+    return TRUE;
+  default:
+    return FALSE;
+  }
+}
+
+static void playlist_picker_select_activate(GtkMenuItem attribute((unused)) *item,
+                                            gpointer attribute((unused)) userdata) {
+  /* nothing */
+}
+
+static int playlist_picker_select_sensitive(void *extra) {
+  GtkTreeIter *iter = extra;
+  return gtk_tree_store_iter_depth(playlist_picker_list, iter) > 0;
+}
+
+static void playlist_picker_play_activate(GtkMenuItem attribute((unused)) *item,
+                                          gpointer attribute((unused)) userdata) {
+  /* Re-use the menu-based activation callback */
+  disorder_eclient_playlist_get(client, playlist_menu_received_content,
+                                playlist_picker_selected, NULL);
+}
+
+static int playlist_picker_play_sensitive(void *extra) {
+  GtkTreeIter *iter = extra;
+  return gtk_tree_store_iter_depth(playlist_picker_list, iter) > 0;
+}
+
+static void playlist_picker_remove_activate(GtkMenuItem attribute((unused)) *item,
+                                            gpointer attribute((unused)) userdata) {
+  /* Re-use the 'Remove' button' */
+  playlist_picker_delete(NULL, NULL);
+}
+
+static int playlist_picker_remove_sensitive(void *extra) {
+  GtkTreeIter *iter = extra;
+  if(gtk_tree_store_iter_depth(playlist_picker_list, iter) > 0) {
+    if(strchr(playlist_picker_selected, '.')) {
+      if(!strncmp(playlist_picker_selected, config->username,
+                  strlen(config->username)))
+        return TRUE;
+    } else
+      return TRUE;
+  }
+  return FALSE;
+}
+
+/** @brief Pop-up menu for picker */
+static struct menuitem playlist_picker_menuitems[] = {
+  {
+    "Select playlist",
+    NULL,
+    playlist_picker_select_activate,
+    playlist_picker_select_sensitive,
+    0,
+    0
+  },
+  {
+    "Play playlist",
+    GTK_STOCK_MEDIA_PLAY, 
+    playlist_picker_play_activate,
+    playlist_picker_play_sensitive,
+    0,
+    0
+  },
+  {
+    "Remove playlist",
+    GTK_STOCK_DELETE,
+    playlist_picker_remove_activate,
+    playlist_picker_remove_sensitive,
+    0,
+    0
+  },
+};
+
+static gboolean playlist_picker_button(GtkWidget *widget,
+                                       GdkEventButton *event,
+                                       gpointer attribute((unused)) user_data) {
+  if(event->type == GDK_BUTTON_PRESS && event->button == 3) {
+    static GtkWidget *playlist_picker_menu;
+
+    /* Right click press pops up a menu */
+    ensure_selected(GTK_TREE_VIEW(widget), event);
+    /* Find the selected row */
+    GtkTreeIter iter[1];
+    if(!gtk_tree_selection_get_selected(playlist_picker_selection, 0, iter))
+      return TRUE;
+    popup(&playlist_picker_menu, event,
+          playlist_picker_menuitems,
+          sizeof playlist_picker_menuitems / sizeof *playlist_picker_menuitems,
+          iter);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+static void playlist_picker_destroy(void) {
+  playlist_picker_delete_button = NULL;
+  g_object_unref(playlist_picker_list);
+  playlist_picker_list = NULL;
+  playlist_picker_selection = NULL;
+  playlist_picker_selected = NULL;
+}
+
+/* Playlist editor ---------------------------------------------------------- */
+
+static GtkWidget *playlist_editor_shared;
+static GtkWidget *playlist_editor_public;
+static GtkWidget *playlist_editor_private;
+static int playlist_editor_setting_buttons;
+
+/** @brief Buttons for the playlist window */
+static struct button playlist_editor_buttons[] = {
+  {
+    GTK_STOCK_OK,
+    playlist_editor_ok,
+    "Close window",
+    0,
+    gtk_box_pack_end,
+  },
+  {
+    GTK_STOCK_HELP,
+    playlist_editor_help,
+    "Go to manual",
+    0,
+    gtk_box_pack_end,
+  },
+};
+
+#define NPLAYLIST_EDITOR_BUTTONS (int)(sizeof playlist_editor_buttons / sizeof *playlist_editor_buttons)
+
+static GtkWidget *playlists_editor_create(void) {
+  assert(ql_playlist.view == NULL);     /* better not be set up already */
+
+  GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
+  playlist_editor_shared = gtk_radio_button_new_with_label(NULL, "shared");
+  playlist_editor_public
+    = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(playlist_editor_shared),
+                                                  "public");
+  playlist_editor_private
+    = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(playlist_editor_shared),
+                                                  "private");
+  g_signal_connect(playlist_editor_public, "toggled",
+                   G_CALLBACK(playlist_editor_button_toggled),
+                   (void *)"public");
+  g_signal_connect(playlist_editor_private, "toggled",
+                   G_CALLBACK(playlist_editor_button_toggled),
+                   (void *)"private");
+  gtk_box_pack_start(GTK_BOX(hbox), playlist_editor_shared,
+                     FALSE/*expand*/, FALSE/*fill*/, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), playlist_editor_public,
+                     FALSE/*expand*/, FALSE/*fill*/, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), playlist_editor_private,
+                     FALSE/*expand*/, FALSE/*fill*/, 0);
+  playlist_editor_set_buttons(0,0,0);
+  create_buttons_box(playlist_editor_buttons,
+                     NPLAYLIST_EDITOR_BUTTONS,
+                     hbox);
+
+  GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
+  GtkWidget *view = init_queuelike(&ql_playlist);
+  gtk_box_pack_start(GTK_BOX(vbox), view,
+                     TRUE/*expand*/, TRUE/*fill*/, 0);
+  gtk_box_pack_start(GTK_BOX(vbox), hbox,
+                     FALSE/*expand*/, FALSE/*fill*/, 0);
+  g_signal_connect(view, "key-press-event",
+                   G_CALLBACK(playlist_editor_keypress), 0);
+  return vbox;
+}
+
+static gboolean playlist_editor_keypress(GtkWidget attribute((unused)) *widget,
+                                         GdkEventKey *event,
+                                         gpointer attribute((unused)) user_data) {
+  if(event->state)
+    return FALSE;
+  switch(event->keyval) {
+  case GDK_BackSpace:
+  case GDK_Delete:
+    playlist_remove_activate(NULL, NULL);
+    return TRUE;
+  default:
+    return FALSE;
+  }
+}
+
+/** @brief Called when the public/private buttons are set */
+static void playlist_editor_button_toggled(GtkToggleButton *tb,
+                                           gpointer userdata) {
+  const char *state = userdata;
+  if(!gtk_toggle_button_get_active(tb)
+     || !playlist_picker_selected
+     || playlist_editor_setting_buttons)
+    return;
+  disorder_eclient_playlist_set_share(client, playlist_editor_share_set,
+                                      playlist_picker_selected, state, NULL);
+}
+
+static void playlist_editor_share_set(void attribute((unused)) *v,
+                                      const attribute((unused)) char *err) {
+  if(err)
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+}
+  
+/** @brief Set the editor button state and sensitivity */
+static void playlist_editor_set_buttons(const char attribute((unused)) *event,
+                                        void *eventdata,
+                                        void attribute((unused)) *callbackdata) {
+  /* If this event is for a non-selected playlist do nothing */
+  if(eventdata
+     && playlist_picker_selected
+     && strcmp(eventdata, playlist_picker_selected))
+    return;
+  if(playlist_picker_selected) {
+    if(strchr(playlist_picker_selected, '.'))
+      disorder_eclient_playlist_get_share(client,
+                                          playlist_editor_got_share,
+                                          playlist_picker_selected,
+                                          (void *)playlist_picker_selected);
+    else
+      playlist_editor_got_share((void *)playlist_picker_selected, NULL,
+                                "shared");
+  } else
+    playlist_editor_got_share(NULL, NULL, NULL);
+}
+
+/** @brief Called with playlist sharing details */
+static void playlist_editor_got_share(void *v,
+                                      const char *err,
+                                      const char *value) {
+  const char *playlist = v;
+  if(err) {
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+    value = NULL;
+  }
+  /* Set the currently active button */
+  ++playlist_editor_setting_buttons;
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_editor_shared),
+                               value && !strcmp(value, "shared"));
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_editor_public),
+                               value && !strcmp(value, "public"));
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_editor_private),
+                               value && !strcmp(value, "private"));
+  /* Set button sensitivity */
+  gtk_widget_set_sensitive(playlist_editor_shared, FALSE);
+  int sensitive = (playlist
+                   && strchr(playlist, '.')
+                   && !strncmp(playlist, config->username,
+                               strlen(config->username)));
+  gtk_widget_set_sensitive(playlist_editor_public, sensitive);
+  gtk_widget_set_sensitive(playlist_editor_private, sensitive);
+  --playlist_editor_setting_buttons;
+}
+
+/** @brief (Re-)populate the playlist tree model */
+static void playlist_editor_fill(const char attribute((unused)) *event,
+                                 void *eventdata,
+                                 void attribute((unused)) *callbackdata) {
+  const char *modified_playlist = eventdata;
+  if(!playlist_window)
+    return;
+  if(!playlist_picker_selected)
+    return;
+  if(!strcmp(playlist_picker_selected, modified_playlist))
+    disorder_eclient_playlist_get(client, playlists_editor_received_tracks,
+                                  playlist_picker_selected,
+                                  (void *)playlist_picker_selected);
+}
+
+/** @brief Called with new tracks for the playlist */
+static void playlists_editor_received_tracks(void *v,
+                                             const char *err,
+                                             int nvec, char **vec) {
+  const char *playlist = v;
+  if(err) {
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+    return;
+  }
+  if(!playlist_picker_selected
+     || strcmp(playlist, playlist_picker_selected)) {
+    /* The tracks are for the wrong playlist - something must have changed
+     * while the fetch command was in flight.  We just ignore this callback,
+     * the right answer will be requested and arrive in due course. */
+    return;
+  }
+  if(nvec == -1)
+    /* No such playlist, presumably we'll get a deleted event shortly */
+    return;
+  /* Translate the list of tracks into queue entries */
+  struct queue_entry *newq, **qq = &newq, *qprev = NULL;
+  hash *h = hash_new(sizeof(int));
+  for(int n = 0; n < nvec; ++n) {
+    struct queue_entry *q = xmalloc(sizeof *q);
+    q->prev = qprev;
+    q->track = vec[n];
+    /* Synthesize a unique ID so that the selection survives updates.  Tracks
+     * can appear more than once in the queue so we can't use raw track names,
+     * so we add a serial number to the start. */
+    int *serialp = hash_find(h, vec[n]), serial = serialp ? *serialp : 0;
+    byte_xasprintf((char **)&q->id, "%d-%s", serial++, vec[n]);
+    hash_add(h, vec[n], &serial, HASH_INSERT_OR_REPLACE);
+    *qq = q;
+    qq = &q->next;
+    qprev = q;
+  }
+  *qq = NULL;
+  ql_new_queue(&ql_playlist, newq);
+}
+
+static void playlist_editor_ok(GtkButton attribute((unused)) *button, 
+                               gpointer attribute((unused)) userdata) {
+  gtk_widget_destroy(playlist_window);
+}
+
+static void playlist_editor_help(GtkButton attribute((unused)) *button, 
+                                 gpointer attribute((unused)) userdata) {
+  popup_help("playlists.html");
+}
+
+/* Playlist mutation -------------------------------------------------------- */
+
+/** @brief State structure for guarded playlist modification
+ *
+ * To safely move, insert or delete rows we must:
+ * - take a lock
+ * - fetch the playlist
+ * - verify it's not changed
+ * - update the playlist contents
+ * - store the playlist
+ * - release the lock
+ *
+ * The playlist_modify_ functions do just that.
+ *
+ * To kick things off create one of these and disorder_eclient_playlist_lock()
+ * with playlist_modify_locked() as its callback.  @c modify will be called; it
+ * should disorder_eclient_playlist_set() to set the new state with
+ * playlist_modify_updated() as its callback.
+ */
+struct playlist_modify_data {
+  /** @brief Affected playlist */
+  const char *playlist;
+  /** @brief Modification function
+   * @param mod Pointer back to state structure
+   * @param ntracks Length of playlist
+   * @param tracks Tracks in playlist
+   */
+  void (*modify)(struct playlist_modify_data *mod,
+                int ntracks, char **tracks);
+
+  /** @brief Number of tracks dropped */
+  int ntracks;
+  /** @brief Track names dropped */
+  char **tracks;
+  /** @brief Track IDs dropped */
+  char **ids;
+  /** @brief Drop after this point */
+  struct queue_entry *after_me;
+};
+
+/** @brief Called with playlist locked
+ *
+ * This is the entry point for guarded modification ising @ref
+ * playlist_modify_data.
+ */
+static void playlist_modify_locked(void *v, const char *err) {
+  struct playlist_modify_data *mod = v;
+  if(err) {
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+    return;
+  }
+  disorder_eclient_playlist_get(client, playlist_modify_retrieved,
+                                mod->playlist, mod);
+}
+
+/** @brief Called with current playlist contents
+ * Checks that the playlist is still current and has not changed.
+ */
+void playlist_modify_retrieved(void *v, const char *err,
+                               int nvec,
+                               char **vec) {
+  struct playlist_modify_data *mod = v;
+  if(err) {
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+    disorder_eclient_playlist_unlock(client, playlist_modify_unlocked, NULL);
+    return;
+  }
+  if(nvec < 0
+     || !playlist_picker_selected
+     || strcmp(mod->playlist, playlist_picker_selected)) {
+    disorder_eclient_playlist_unlock(client, playlist_modify_unlocked, NULL);
+    return;
+  }
+  /* We check that the contents haven't changed.  If they have we just abandon
+   * the operation.  The user will have to try again. */
+  struct queue_entry *q;
+  int n;
+  for(n = 0, q = ql_playlist.q; q && n < nvec; ++n, q = q->next)
+    if(strcmp(q->track, vec[n]))
+      break;
+  if(n != nvec || q != NULL)  {
+    disorder_eclient_playlist_unlock(client, playlist_modify_unlocked, NULL);
+    return;
+  }
+  mod->modify(mod, nvec, vec);
+}
+
+/** @brief Called when the playlist has been updated */
+static void playlist_modify_updated(void attribute((unused)) *v,
+                                    const char *err) {
+  if(err) 
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+  disorder_eclient_playlist_unlock(client, playlist_modify_unlocked, NULL);
+}
+
+/** @brief Called when the playlist has been unlocked */
+static void playlist_modify_unlocked(void attribute((unused)) *v,
+                                     const char *err) {
+  if(err) 
+    popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
+}
+
+/* Drop tracks into a playlist ---------------------------------------------- */
+
+static void playlist_drop(struct queuelike attribute((unused)) *ql,
+                          int ntracks,
+                          char **tracks, char **ids,
+                          struct queue_entry *after_me) {
+  struct playlist_modify_data *mod = xmalloc(sizeof *mod);
+
+  mod->playlist = playlist_picker_selected;
+  mod->modify = playlist_drop_modify;
+  mod->ntracks = ntracks;
+  mod->tracks = tracks;
+  mod->ids = ids;
+  mod->after_me = after_me;
+  disorder_eclient_playlist_lock(client, playlist_modify_locked,
+                                 mod->playlist, mod);
+}
+
+/** @brief Return true if track @p i is in the moved set */
+static int playlist_drop_is_moved(struct playlist_modify_data *mod,
+                                  int i) {
+  struct queue_entry *q;
+
+  /* Find the q corresponding to i, so we can get the ID */
+  for(q = ql_playlist.q; i; q = q->next, --i)
+    ;
+  /* See if track i matches any of the moved set by ID */
+  for(int n = 0; n < mod->ntracks; ++n)
+    if(!strcmp(q->id, mod->ids[n]))
+      return 1;
+  return 0;
+}
+
+static void playlist_drop_modify(struct playlist_modify_data *mod,
+                                 int nvec, char **vec) {
+  char **newvec;
+  int nnewvec;
+
+  //fprintf(stderr, "\nplaylist_drop_modify\n");
+  /* after_me is the queue_entry to insert after, or NULL to insert at the
+   * beginning (including the case when the playlist is empty) */
+  //fprintf(stderr, "after_me = %s\n",
+  //        mod->after_me ? mod->after_me->track : "NULL");
+  struct queue_entry *q = ql_playlist.q;
+  int ins = 0;
+  if(mod->after_me) {
+    ++ins;
+    while(q && q != mod->after_me) {
+      q = q->next;
+      ++ins;
+    }
+  }
+  /* Now ins is the index to insert at; equivalently, the row to insert before,
+   * and so equal to nvec to append. */
+#if 0
+  fprintf(stderr, "ins = %d = %s\n",
+          ins, ins < nvec ? vec[ins] : "NULL");
+  for(int n = 0; n < nvec; ++n)
+    fprintf(stderr, "%d: %s %s\n", n, n == ins ? "->" : "  ", vec[n]);
+  fprintf(stderr, "nvec = %d\n", nvec);
+#endif
+  if(mod->ids) {
+    /* This is a rearrangement */
+    /* We have:
+     * - vec[], the current layout
+     * - ins, pointing into vec
+     * - mod->tracks[], a subset of vec[] which is to be moved
+     *
+     * ins is the insertion point BUT it is in terms of the whole
+     * array, i.e. before mod->tracks[] have been removed.  The first
+     * step then is to remove everything in mod->tracks[] and adjust
+     * ins downwards as necessary.
+     */
+    /* First zero out anything that's moved */
+    int before_ins = 0;
+    for(int n = 0; n < nvec; ++n) {
+      if(playlist_drop_is_moved(mod, n)) {
+        vec[n] = NULL;
+        if(n < ins)
+          ++before_ins;
+      }
+    }
+    /* Now collapse down the array */
+    int i = 0;
+    for(int n = 0; n < nvec; ++n) {
+      if(vec[n])
+        vec[i++] = vec[n];
+    }
+    assert(i + mod->ntracks == nvec);
+    nvec = i;
+    /* Adjust the insertion point to take account of things moved from before
+     * it */
+    ins -= before_ins;
+    /* The effect is now the same as an insertion */
+  }
+  /* This is (now) an insertion */
+  nnewvec = nvec + mod->ntracks;
+  newvec = xcalloc(nnewvec, sizeof (char *));
+  memcpy(newvec, vec,
+         ins * sizeof (char *));
+  memcpy(newvec + ins, mod->tracks,
+         mod->ntracks * sizeof (char *));
+  memcpy(newvec + ins + mod->ntracks, vec + ins,
+         (nvec - ins) * sizeof (char *));
+  disorder_eclient_playlist_set(client, playlist_modify_updated, mod->playlist,
+                                newvec, nnewvec, mod);
+}
+
+/* Playlist editor right-click menu ---------------------------------------- */
+
+/** @brief Called to determine whether the playlist is playable */
+static int playlist_playall_sensitive(void attribute((unused)) *extra) {
+  /* If there's no playlist obviously we can't play it */
+  if(!playlist_picker_selected)
+    return FALSE;
+  /* If it's empty we can't play it */
+  if(!ql_playlist.q)
+    return FALSE;
+  /* Otherwise we can */
+  return TRUE;
+}
+
+/** @brief Called to play the selected playlist */
+static void playlist_playall_activate(GtkMenuItem attribute((unused)) *menuitem,
+                                      gpointer attribute((unused)) user_data) {
+  if(!playlist_picker_selected)
+    return;
+  /* Re-use the menu-based activation callback */
+  disorder_eclient_playlist_get(client, playlist_menu_received_content,
+                                playlist_picker_selected, NULL);
+}
+
+/** @brief Called to determine whether the playlist is playable */
+static int playlist_remove_sensitive(void attribute((unused)) *extra) {
+  /* If there's no playlist obviously we can't remove from it */
+  if(!playlist_picker_selected)
+    return FALSE;
+  /* If no tracks are selected we cannot remove them */
+  if(!gtk_tree_selection_count_selected_rows(ql_playlist.selection))
+    return FALSE;
+  /* We're good to go */
+  return TRUE;
+}
+
+/** @brief Called to remove the selected playlist */
+static void playlist_remove_activate(GtkMenuItem attribute((unused)) *menuitem,
+                                     gpointer attribute((unused)) user_data) {
+  if(!playlist_picker_selected)
+    return;
+  struct playlist_modify_data *mod = xmalloc(sizeof *mod);
+
+  mod->playlist = playlist_picker_selected;
+  mod->modify = playlist_remove_modify;
+  disorder_eclient_playlist_lock(client, playlist_modify_locked,
+                                 mod->playlist, mod);
+}
+
+static void playlist_remove_modify(struct playlist_modify_data *mod,
+                                   int attribute((unused)) nvec, char **vec) {
+  GtkTreeIter iter[1];
+  gboolean it = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ql_playlist.store),
+                                              iter);
+  int n = 0, m = 0;
+  while(it) {
+    if(!gtk_tree_selection_iter_is_selected(ql_playlist.selection, iter))
+      vec[m++] = vec[n++];
+    else
+      n++;
+    it = gtk_tree_model_iter_next(GTK_TREE_MODEL(ql_playlist.store), iter);
+  }
+  disorder_eclient_playlist_set(client, playlist_modify_updated, mod->playlist,
+                                vec, m, mod);
+}
+
+/* Playlists window --------------------------------------------------------- */
+
+/** @brief Pop up the playlists window
+ *
+ * Called when the playlists menu item is selected
+ */
+void playlist_window_create(gpointer attribute((unused)) callback_data,
+                            guint attribute((unused)) callback_action,
+                            GtkWidget attribute((unused)) *menu_item) {
+  /* If the window already exists, raise it */
+  if(playlist_window) {
+    gtk_window_present(GTK_WINDOW(playlist_window));
+    return;
+  }
+  /* Create the window */
+  playlist_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_style(playlist_window, tool_style);
+  g_signal_connect(playlist_window, "destroy",
+                  G_CALLBACK(playlist_window_destroyed), &playlist_window);
+  gtk_window_set_title(GTK_WINDOW(playlist_window), "Playlists Management");
+  /* TODO loads of this is very similar to (copied from!) users.c - can we
+   * de-dupe? */
+  /* Keyboard shortcuts */
+  g_signal_connect(playlist_window, "key-press-event",
+                   G_CALLBACK(playlist_window_keypress), 0);
+  /* default size is too small */
+  gtk_window_set_default_size(GTK_WINDOW(playlist_window), 640, 320);
+
+  GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), playlist_picker_create(),
+                     FALSE/*expand*/, FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), gtk_event_box_new(),
+                     FALSE/*expand*/, FALSE, 2);
+  gtk_box_pack_start(GTK_BOX(hbox), playlists_editor_create(),
+                     TRUE/*expand*/, TRUE/*fill*/, 0);
+
+  gtk_container_add(GTK_CONTAINER(playlist_window), frame_widget(hbox, NULL));
+  gtk_widget_show_all(playlist_window);
+}
+
+/** @brief Keypress handler */
+static gboolean playlist_window_keypress(GtkWidget attribute((unused)) *widget,
+                                         GdkEventKey *event,
+                                         gpointer attribute((unused)) user_data) {
+  if(event->state)
+    return FALSE;
+  switch(event->keyval) {
+  case GDK_Escape:
+    gtk_widget_destroy(playlist_window);
+    return TRUE;
+  default:
+    return FALSE;
+  }
+}
+
+/** @brief Called when the playlist window is destroyed */
+static void playlist_window_destroyed(GtkWidget attribute((unused)) *widget,
+                                      GtkWidget **widget_pointer) {
+  destroy_queuelike(&ql_playlist);
+  playlist_picker_destroy();
+  *widget_pointer = NULL;
+}
+
+/** @brief Initialize playlist support */
+void playlists_init(void) {
+  /* We re-get all playlists upon any change... */
+  event_register("playlist-created", playlist_list_update, 0);
+  event_register("playlist-deleted", playlist_list_update, 0);
+  /* ...and on reconnection */
+  event_register("log-connected", playlist_list_update, 0);
+  /* ...and from time to time */
+  event_register("periodic-slow", playlist_list_update, 0);
+  /* ...and at startup */
+  playlist_list_update(0, 0, 0);
+
+  /* Update the playlists menu when the set of playlists changes */
+  event_register("playlists-updated", playlist_menu_changed, 0);
+  /* Update the new-playlist OK button when the set of playlists changes */
+  event_register("playlists-updated", playlist_new_changed, 0);
+  /* Update the list of playlists in the edit window when the set changes */
+  event_register("playlists-updated", playlist_picker_fill, 0);
+  /* Update the displayed playlist when it is modified */
+  event_register("playlist-modified", playlist_editor_fill, 0);
+  /* Update the shared/public/etc buttons when a playlist is modified */
+  event_register("playlist-modified", playlist_editor_set_buttons, 0);
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 6613cfc..d7e91f3 100644 (file)
@@ -34,7 +34,17 @@ void popup(GtkWidget **menup,
     g_signal_connect(menu, "destroy",
                      G_CALLBACK(gtk_widget_destroyed), menup);
     for(int n = 0; n < nitems; ++n) {
-      items[n].w = gtk_menu_item_new_with_label(items[n].name);
+      if(items[n].stock) {
+        GtkWidget *image = gtk_image_new_from_stock(items[n].stock,
+                                                    GTK_ICON_SIZE_MENU);
+        items[n].w = gtk_image_menu_item_new_with_label(items[n].name);
+        gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(items[n].w),
+                                      image);
+      } else
+        items[n].w = gtk_menu_item_new_with_label(items[n].name);
+      /* TODO accelerators would be useful here.  There might be some
+       * interaction with the main menu accelerators, _except_ for playlist
+       * case!  */
       gtk_menu_attach(GTK_MENU(menu), items[n].w, 0, 1, n, n + 1);
     }
     set_tool_colors(menu);
index 9706e03..25726b2 100644 (file)
@@ -26,6 +26,9 @@ struct menuitem {
   /** @brief Menu item name */
   const char *name;
 
+  /** @brief Stock icon name */
+  const char *stock;
+
   /** @brief Called to activate the menu item */
   void (*activate)(GtkMenuItem *menuitem,
                    gpointer user_data);
index 2e31603..33dbfc9 100644 (file)
@@ -30,12 +30,14 @@ struct progress_window {
 };
 
 /** @brief Create a progress window */
-struct progress_window *progress_window_new(const char *title) {
+struct progress_window *progress_window_new(const char *title,
+                                            GtkWidget *parent) {
   struct progress_window *pw = xmalloc(sizeof *pw);
 
   pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-  gtk_window_set_transient_for(GTK_WINDOW(pw->window),
-                               GTK_WINDOW(toplevel));
+  if(parent)
+    gtk_window_set_transient_for(GTK_WINDOW(pw->window),
+                                 GTK_WINDOW(parent));
   g_signal_connect(pw->window, "destroy",
                   G_CALLBACK(gtk_widget_destroyed), &pw->window);
   gtk_window_set_default_size(GTK_WINDOW(pw->window), 360, -1);
index 62ea22f..6600cf9 100644 (file)
@@ -46,6 +46,7 @@ static void prefdata_completed(void *v, const char *err, const char *value);
 static void properties_ok(GtkButton *button, gpointer userdata);
 static void properties_apply(GtkButton *button, gpointer userdata);
 static void properties_cancel(GtkButton *button, gpointer userdata);
+static void properties_help(GtkButton *button, gpointer userdata);
 
 static void properties_logged_in(const char *event,
                                  void *eventdata,
@@ -126,22 +127,32 @@ static const struct pref {
 /* Buttons that appear at the bottom of the window */
 static struct button buttons[] = {
   {
+    GTK_STOCK_HELP,
+    properties_help,
+    "Go to manual",
+    0,
+    gtk_box_pack_start,
+  },
+  {
     GTK_STOCK_OK,
     properties_ok,
     "Apply all changes and close window",
-    0
-  },
-  {
-    GTK_STOCK_APPLY,
-    properties_apply,
-    "Apply all changes and keep window open",
-    0
+    0,
+    gtk_box_pack_end,
   },
   {
     GTK_STOCK_CANCEL,
     properties_cancel,
     "Discard all changes and close window",
-    0
+    0,
+    gtk_box_pack_end
+  },
+  {
+    GTK_STOCK_APPLY,
+    properties_apply,
+    "Apply all changes and keep window open",
+    0,
+    gtk_box_pack_end,
   },
 };
 
@@ -186,7 +197,8 @@ static gboolean properties_keypress(GtkWidget attribute((unused)) *widget,
   }
 }
 
-void properties(int ntracks, const char **tracks) {
+void properties(int ntracks, const char **tracks,
+                GtkWidget *parent) {
   int n, m;
   struct prefdata *f;
   GtkWidget *buttonbox, *vbox, *label, *entry, *propagate;
@@ -299,7 +311,9 @@ void properties(int ntracks, const char **tracks) {
   if(pw)
     progress_window_progress(pw, 0, 0);
   /* Pop up a progress bar while we're waiting */
-  pw = progress_window_new("Fetching Track Properties");
+  while(parent->parent)
+    parent = parent->parent;
+  pw = progress_window_new("Fetching Track Properties", parent);
 }
 
 /* Everything is filled in now */
@@ -487,6 +501,11 @@ static void properties_cancel(GtkButton attribute((unused)) *button,
   properties_event = 0;
 }
 
+static void properties_help(GtkButton attribute((unused)) *button,
+                            gpointer attribute((unused)) userdata) {
+  popup_help("properties.html");
+}
+
 /** @brief Called when we've just logged in
  *
  * Destroys the current properties window.
index 636f10c..333187a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder
- * Copyright (C) 2006-2008 Richard Kettlewell
+ * Copyright (C) 2006-2009 Richard Kettlewell
  *
  * 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
 #include "disobedience.h"
 #include "popup.h"
 #include "queue-generic.h"
-
-static struct queuelike *const queuelikes[] = {
-  &ql_queue, &ql_recent, &ql_added
-};
-#define NQUEUELIKES (sizeof queuelikes / sizeof *queuelikes)
+#include "multidrag.h"
+#include "autoscroll.h"
 
 /* Track detail lookup ----------------------------------------------------- */
 
@@ -129,7 +126,7 @@ const char *column_length(const struct queue_entry *q,
     else {
       if(!last_playing)
         return NULL;
-      time(&now);
+      xtime(&now);
       l = playing_track->sofar + (now - last_playing);
     }
     byte_xasprintf(&played, "%ld:%02ld/%s", l / 60, l % 60, length);
@@ -143,7 +140,7 @@ const char *column_length(const struct queue_entry *q,
 /** @brief Return the @ref queue_entry corresponding to @p iter
  * @param model Model that owns @p iter
  * @param iter Tree iterator
- * @return ID string
+ * @return Pointer to queue entry
  */
 struct queue_entry *ql_iter_to_q(GtkTreeModel *model,
                                  GtkTreeIter *iter) {
@@ -157,6 +154,19 @@ struct queue_entry *ql_iter_to_q(GtkTreeModel *model,
   return q;
 }
 
+/** @brief Return the @ref queue_entry corresponding to @p path
+ * @param model Model to query
+ * @param path Path into tree
+ * @return Pointer to queue entry or NULL
+ */
+struct queue_entry *ql_path_to_q(GtkTreeModel *model,
+                                 GtkTreePath *path) {
+  GtkTreeIter iter[1];
+  if(!gtk_tree_model_get_iter(model, iter, path))
+    return NULL;
+  return ql_iter_to_q(model, iter);
+}
+
 /** @brief Update one row of a list store
  * @param q Queue entry
  * @param iter Iterator referring to row or NULL to work it out
@@ -232,22 +242,27 @@ static void record_queue_map(hash *h,
     hash_add(h, id, empty, HASH_INSERT);
     nqd = hash_find(h, id);
   }
-  if(old)
+  if(old) {
+#if DEBUG_QUEUE
+    fprintf(stderr, " old: %s\n", id);
+#endif
     nqd->old = old;
-  if(new)
+  }
+  if(new) {
+#if DEBUG_QUEUE
+    fprintf(stderr, " new: %s\n", id);
+#endif
     nqd->new = new;
+  }
 }
 
-#if 0
+#if DEBUG_QUEUE
 static void dump_queue(struct queue_entry *head, struct queue_entry *mark) {
   for(struct queue_entry *q = head; q; q = q->next) {
     if(q == mark)
-      fprintf(stderr, "!");
-    fprintf(stderr, "%s", q->id);
-    if(q->next)
-      fprintf(stderr, " ");
+      fprintf(stderr, " !");
+    fprintf(stderr, " %s\n", q->id);
   }
-  fprintf(stderr, "\n");
 }
 
 static void dump_rows(struct queuelike *ql) {
@@ -257,11 +272,8 @@ static void dump_rows(struct queuelike *ql) {
   while(it) {
     struct queue_entry *q = ql_iter_to_q(GTK_TREE_MODEL(ql->store), iter);
     it = gtk_tree_model_iter_next(GTK_TREE_MODEL(ql->store), iter);
-    fprintf(stderr, "%s", q->id);
-    if(it)
-      fprintf(stderr, " ");
+    fprintf(stderr, " %s\n", q->id);
   }
-  fprintf(stderr, "\n");
 }
 #endif
 
@@ -277,11 +289,15 @@ void ql_new_queue(struct queuelike *ql,
   ++suppress_actions;
 
   /* Tell every queue entry which queue owns it */
-  //fprintf(stderr, "%s: filling in q->ql\n", ql->name);
+#if DEBUG_QUEUE
+  fprintf(stderr, "%s: filling in q->ql\n", ql->name);
+#endif
   for(struct queue_entry *q = newq; q; q = q->next)
     q->ql = ql;
 
-  //fprintf(stderr, "%s: constructing h\n", ql->name);
+#if DEBUG_QUEUE
+  fprintf(stderr, "%s: constructing h\n", ql->name);
+#endif
   /* Construct map from id to new and old structures */
   hash *h = hash_new(sizeof(struct newqueue_data));
   for(struct queue_entry *q = ql->q; q; q = q->next)
@@ -291,7 +307,9 @@ void ql_new_queue(struct queuelike *ql,
 
   /* The easy bit: delete rows not present any more.  In the same pass we
    * update the secret column containing the queue_entry pointer. */
-  //fprintf(stderr, "%s: deleting rows...\n", ql->name);
+#if DEBUG_QUEUE
+  fprintf(stderr, "%s: deleting rows...\n", ql->name);
+#endif
   GtkTreeIter iter[1];
   gboolean it = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ql->store),
                                               iter);
@@ -305,10 +323,14 @@ void ql_new_queue(struct queuelike *ql,
                          ql->ncolumns + QUEUEPOINTER_COLUMN, nqd->new,
                          -1);
       it = gtk_tree_model_iter_next(GTK_TREE_MODEL(ql->store), iter);
+      /* We'll need the new start time */
+      nqd->new->when = q->when;
       ++kept;
     } else {
       /* Delete this row (and move iter to the next one) */
-      //fprintf(stderr, " delete %s", q->id);
+#if DEBUG_QUEUE
+      fprintf(stderr, " delete %s\n", q->id);
+#endif
       it = gtk_list_store_remove(ql->store, iter);
       ++deleted;
     }
@@ -319,7 +341,9 @@ void ql_new_queue(struct queuelike *ql,
 
   /* We're going to have to support arbitrary rearrangements, so we might as
    * well add new elements at the end. */
-  //fprintf(stderr, "%s: adding rows...\n", ql->name);
+#if DEBUG_QUEUE
+  fprintf(stderr, "%s: adding rows...\n", ql->name);
+#endif
   struct queue_entry *after = 0;
   for(struct queue_entry *q = newq; q; q = q->next) {
     const struct newqueue_data *nqd = hash_find(h, q->id);
@@ -340,7 +364,9 @@ void ql_new_queue(struct queuelike *ql,
       gtk_list_store_set(ql->store, iter,
                          ql->ncolumns + QUEUEPOINTER_COLUMN, q,
                          -1);
-      //fprintf(stderr, " add %s", q->id);
+#if DEBUG_QUEUE
+      fprintf(stderr, " add %s\n", q->id);
+#endif
       ++inserted;
     }
     after = newq;
@@ -353,55 +379,408 @@ void ql_new_queue(struct queuelike *ql,
    * The current code is simple but amounts to a bubble-sort - we might easily
    * called gtk_tree_model_iter_next a couple of thousand times.
    */
-  //fprintf(stderr, "%s: rearranging rows\n", ql->name);
-  //fprintf(stderr, "%s: queue state: ", ql->name);
-  //dump_queue(newq, 0);
-  //fprintf(stderr, "%s: row state: ", ql->name);
-  //dump_rows(ql);
-  it = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ql->store),
-                                              iter);
-  struct queue_entry *rq = newq;        /* r for 'right, correct' */
+#if DEBUG_QUEUE
+  fprintf(stderr, "%s: rearranging rows\n", ql->name);
+  fprintf(stderr, "%s: target state:\n", ql->name);
+  dump_queue(newq, 0);
+  fprintf(stderr, "%s: current state:\n", ql->name);
+  dump_rows(ql);
+#endif
+  it = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ql->store), iter);
+  struct queue_entry *tq = newq;        /* t-for-target */
   int swaps = 0, searches = 0;
+  int row = 0;
   while(it) {
-    struct queue_entry *q = ql_iter_to_q(GTK_TREE_MODEL(ql->store), iter);
-    //fprintf(stderr, " rq = %p, q = %p\n", rq, q);
-    //fprintf(stderr, " rq->id = %s, q->id = %s\n", rq->id, q->id);
-
-    if(q != rq) {
-      //fprintf(stderr, "  mismatch\n");
+    struct queue_entry *cq = ql_iter_to_q(GTK_TREE_MODEL(ql->store), iter);
+    /* c-for-current */
+
+    /* Everything has the right queue pointer (see above) so it's sufficient to
+     * compare pointers to detect mismatches */
+    if(cq != tq) {
+#if DEBUG_QUEUE
+      fprintf(stderr, "  pointer mismatch at row %d\n", row);
+      fprintf(stderr, "   target id %s\n", tq->id);
+      fprintf(stderr, "   actual id %s\n", cq->id);
+#endif
+      /* Start looking for the target row fromn the next row */
       GtkTreeIter next[1] = { *iter };
       gboolean nit = gtk_tree_model_iter_next(GTK_TREE_MODEL(ql->store), next);
       while(nit) {
         struct queue_entry *nq = ql_iter_to_q(GTK_TREE_MODEL(ql->store), next);
-        //fprintf(stderr, "   candidate: %s\n", nq->id);
-        if(nq == rq)
+#if DEBUG_QUEUE
+        fprintf(stderr, "   candidate: %s\n", nq->id);
+#endif
+        if(nq == tq)
           break;
         nit = gtk_tree_model_iter_next(GTK_TREE_MODEL(ql->store), next);
         ++searches;
       }
+      /* Note that this assertion will fail in the face of duplicate IDs.
+       * q->id really does need to be unique. */
       assert(nit);
-      //fprintf(stderr, "  found it\n");
       gtk_list_store_swap(ql->store, iter, next);
       *iter = *next;
-      //fprintf(stderr, "%s: new row state: ", ql->name);
-      //dump_rows(ql);
+#if DEBUG_QUEUE
+      fprintf(stderr, "%s: found it.  new row state:\n", ql->name);
+      dump_rows(ql);
+#endif
       ++swaps;
     }
     /* ...and onto the next one */
     it = gtk_tree_model_iter_next(GTK_TREE_MODEL(ql->store), iter);
-    rq = rq->next;
+    tq = tq->next;
+    ++row;
   }
-#if 0
+#if DEBUG_QUEUE
   fprintf(stderr, "%6s: %3d kept %3d inserted %3d deleted %3d swaps %4d searches\n", ql->name,
           kept, inserted, deleted, swaps, searches);
+  fprintf(stderr, "done\n");
 #endif
-  //fprintf(stderr, "done\n");
   ql->q = newq;
   /* Set the rest of the columns in new rows */
   ql_update_list_store(ql);
   --suppress_actions;
 }
 
+/* Drag and drop ------------------------------------------------------------ */
+
+/** @brief Identify the drop path
+ * @param w Destination tree view widget
+ * @param model Underlying tree model
+ * @param wx X coordinate
+ * @param wy Y coordinate
+ * @param posp Where to store relative position
+ * @return Target path or NULL
+ *
+ * This is used by ql_drag_motion() and ql_drag_data_received() to identify a
+ * drop would or does land.  It's important that they use the same code since
+ * otherwise the visual feedback can be inconsistent with the actual effect!
+ *
+ * Remember to free the returned path.
+ */
+static GtkTreePath *ql_drop_path(GtkWidget *w,
+                                 GtkTreeModel *model,
+                                 int wx, int wy,
+                                 GtkTreeViewDropPosition *posp) {
+  GtkTreePath *path = NULL;
+  GtkTreeViewDropPosition pos;
+  GtkTreeIter iter[1], last[1];
+  int tx, ty;
+
+  gtk_tree_view_convert_widget_to_tree_coords(GTK_TREE_VIEW(w),
+                                              wx, wy, &tx, &ty);
+  if(gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(w),
+                                       wx, wy,
+                                       &path,
+                                       &pos)) {
+    //fprintf(stderr, "gtk_tree_view_get_dest_row_at_pos() -> TRUE\n");
+    // Normalize drop position
+    switch(pos) {
+    case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
+      pos = GTK_TREE_VIEW_DROP_BEFORE;
+      break;
+    case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
+      pos = GTK_TREE_VIEW_DROP_AFTER;
+      break;
+    default: break;
+    }
+  } else if(gtk_tree_model_get_iter_first(model, iter)) {
+    /* If the pointer isn't over any particular row then either it's below
+     * the last row, in which case we want the dropzone to be below that row;
+     * or it's above the first row (in the column headings) in which case we
+     * want the dropzone to be above that row. */
+    if(ty >= 0) {
+      /* Find the last row */
+      do {
+        *last = *iter;
+      } while(gtk_tree_model_iter_next(model, iter));
+      /* The drop target is just after it */
+      pos = GTK_TREE_VIEW_DROP_AFTER;
+      *iter = *last;
+    } else {
+      /* The drop target will be just before the first row */
+      pos = GTK_TREE_VIEW_DROP_BEFORE;
+    }
+    path = gtk_tree_model_get_path(model, iter);
+  }
+  *posp = pos;
+  return path;
+}
+
+#if 0
+static const char *act(GdkDragAction action) {
+  struct dynstr d[1];
+
+  dynstr_init(d);
+  if(action & GDK_ACTION_DEFAULT)
+    dynstr_append_string(d, "|DEFAULT");
+  if(action & GDK_ACTION_COPY)
+    dynstr_append_string(d, "|COPY");
+  if(action & GDK_ACTION_MOVE)
+    dynstr_append_string(d, "|MOVE");
+  if(action & GDK_ACTION_LINK)
+    dynstr_append_string(d, "|LINK");
+  if(action & GDK_ACTION_PRIVATE)
+    dynstr_append_string(d, "|PRIVATE");
+  if(action & GDK_ACTION_ASK)
+    dynstr_append_string(d, "|ASK");
+  dynstr_terminate(d);
+  return d->nvec ? d->vec + 1 : "";
+}
+#endif
+
+/** @brief Called when a drag moves within a candidate destination
+ * @param w Destination widget
+ * @param dc Drag context
+ * @param x Current pointer location
+ * @param y Current pointer location
+ * @param time_ Current time
+ * @param user_data Pointer to queuelike
+ * @return TRUE in a dropzone, otherwise FALSE
+ *
+ * This is the handler for the "drag-motion" signal.
+ */
+static gboolean ql_drag_motion(GtkWidget *w,
+                               GdkDragContext *dc,
+                               gint x,
+                               gint y,
+                               guint time_,
+                               gpointer user_data) {
+  struct queuelike *const ql = user_data;
+  GdkDragAction action = 0;
+
+  // GTK_DEST_DEFAULT_MOTION vets actions as follows:
+  // 1) if dc->suggested_action is in the gtk_drag_dest_set actions
+  //    then dc->suggested_action is taken as the action.
+  // 2) otherwise if dc->actions intersects the gtk_drag_dest_set actions
+  //    then the lowest-numbered member of the intersection is chosen.
+  // 3) otherwise no member is chosen and gdk_drag_status() is called
+  //    with action=0 to refuse the drop.
+  if(dc->suggested_action) {
+    if(dc->suggested_action & (GDK_ACTION_MOVE|GDK_ACTION_COPY))
+      action = dc->suggested_action;
+  } else if(dc->actions & GDK_ACTION_MOVE)
+    action = GDK_ACTION_MOVE;
+  else if(dc->actions & GDK_ACTION_COPY)
+    action = GDK_ACTION_COPY;
+  /* TODO this comes up with the wrong answer sometimes.  If we are in the
+   * middle of a rearrange then the suggested action will be COPY, which we'll
+   * take, even though MOVE would actually be appropriate.  The drag still
+   * seems to work, but it _is_ wrong.  The answer is to take the target into
+   * account. */
+  /*fprintf(stderr, "suggested %s actions %s result %s\n",
+          act(dc->suggested_action), act(dc->actions), act(action));*/
+  if(action) {
+    // If the action is acceptable then we see if this widget is acceptable
+    if(gtk_drag_dest_find_target(w, dc, NULL) == GDK_NONE)
+      action = 0;
+  }
+  // Report the status
+  //fprintf(stderr, "drag action: %u\n", action);
+  gdk_drag_status(dc, action, time_);
+  if(action) {
+    GtkTreeViewDropPosition pos;
+
+    // Find the drop target
+    GtkTreePath *path = ql_drop_path(w, GTK_TREE_MODEL(ql->store), x, y, &pos);
+    // Highlight drop target
+    gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(w), path, pos);
+    if(path)
+      gtk_tree_path_free(path);
+  }
+  autoscroll_add(GTK_TREE_VIEW(w));
+  return TRUE;                          /* We are (always) in a drop zone */
+}
+
+/** @brief Called when a drag moves leaves a candidate destination
+ * @param w Destination widget
+ * @param dc Drag context
+ * @param time_ Current time
+ * @param user_data Pointer to queuelike
+ *
+ * This is the handler for the "drag-leave" signal.
+ *
+ * It turns out that we get a drag-leave event when the data is dropped, too
+ * (See _gtk_drag_dest_handle_event).  This seems logically consistent and is
+ * convenient too - for instance it's why autoscroll_remove() gets called at
+ * the end of a drag+drop sequence.
+ */
+static void ql_drag_leave(GtkWidget *w,
+                          GdkDragContext attribute((unused)) *dc,
+                          guint attribute((unused)) time_,
+                          gpointer attribute((unused)) user_data) {
+  //struct queuelike *const ql = user_data;
+
+  gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(w), NULL, 0);
+  autoscroll_remove(GTK_TREE_VIEW(w));
+}
+
+/** @brief Callback to add selected tracks to the selection data
+ *
+ * Called from ql_drag_data_get().
+ */
+static void ql_drag_data_get_collect(GtkTreeModel *model,
+                                     GtkTreePath attribute((unused)) *path,
+                                     GtkTreeIter *iter,
+                                     gpointer data) {
+  struct dynstr *const result = data;
+  struct queue_entry *const q = ql_iter_to_q(model, iter);
+
+  dynstr_append_string(result, q->id);
+  dynstr_append(result, '\n');
+  dynstr_append_string(result, q->track);
+  dynstr_append(result, '\n');
+}
+
+/** @brief Called to extract the dragged data from the source queuelike
+ * @param w Source widget (the tree view)
+ * @param dc Drag context
+ * @param data Where to put the answer
+ * @param info_ Target @c info parameter
+ * @param time_ Time data requested (for some reason not a @c time_t)
+ * @param user_data The queuelike
+ *
+ * The list of tracks is converted into a single string, consisting of IDs
+ * and track names.  Each is terminated by a newline.  Including both ID and
+ * track name means that the receiver can use whichever happens to be more
+ * convenient.
+ *
+ * If there are no IDs for rows in this widget then the ID half is undefined.
+ *
+ * This is the handler for the "drag-data-get" signal.
+ */
+static void ql_drag_data_get(GtkWidget attribute((unused)) *w,
+                             GdkDragContext attribute((unused)) *dc,
+                             GtkSelectionData *data,
+                             guint attribute((unused)) info,
+                             guint attribute((unused)) time_,
+                             gpointer user_data) {
+  struct queuelike *const ql = user_data;
+  struct dynstr result[1];
+
+  //fprintf(stderr, "ql_drag_data_get %s info=%d\n", ql->name, info);
+  dynstr_init(result);
+  gtk_tree_selection_selected_foreach(ql->selection,
+                                      ql_drag_data_get_collect,
+                                      result);
+  // TODO must not be able to drag playing track!
+  //fprintf(stderr, "drag-data-get: %.*s\n",
+  //        result->nvec, result->vec);
+  /* gtk_selection_data_set_text() insists that data->target is one of a
+   * variety of stringy atoms.  TODO: where does this value actually come
+   * from?  */
+  gtk_selection_data_set(data,
+                         GDK_TARGET_STRING,
+                         8, (guchar *)result->vec, result->nvec);
+}
+
+/** @brief Called when drag data is received
+ * @param w Target widget (the tree view)
+ * @param dc Drag context
+ * @param x The drop location
+ * @param y The drop location
+ * @param data The selection data
+ * @param info_ The target type that was chosen
+ * @param time_ Time data received (for some reason not a @c time_t)
+ * @param user_data The queuelike
+ *
+ * This is the handler for the "drag-data-received" signal.
+ */
+static void ql_drag_data_received(GtkWidget attribute((unused)) *w,
+                                  GdkDragContext attribute((unused)) *dc,
+                                  gint x,
+                                  gint y,
+                                  GtkSelectionData *data,
+                                  guint info_,
+                                  guint attribute((unused)) time_,
+                                  gpointer user_data) {
+  struct queuelike *const ql = user_data;
+  char *result, *p;
+  struct vector ids[1], tracks[1];
+  int parity = 0;
+
+  //fprintf(stderr, "drag-data-received: %d,%d info=%u\n", x, y, info_);
+  /* Get the selection string */
+  p = result = (char *)gtk_selection_data_get_text(data);
+  if(!result) {
+    //fprintf(stderr, "gtk_selection_data_get_text() returned NULL\n");
+    return;
+  }
+  //fprintf(stderr, "%s--\n", result);
+  /* Parse it back into IDs and track names */
+  vector_init(ids);
+  vector_init(tracks);
+  while(*p) {
+    char *nl = strchr(p, '\n');
+    if(!nl)
+      break;
+    *nl = 0;
+    //fprintf(stderr, "  %s\n", p);
+    vector_append(parity++ & 1 ? tracks : ids, xstrdup(p));
+    p = nl + 1;
+  }
+  g_free(result);
+  if(ids->nvec != tracks->nvec) {
+    //fprintf(stderr, "  inconsistent drag data!\n");
+    return;
+  }
+  vector_terminate(ids);
+  vector_terminate(tracks);
+  /* Figure out which row the drop precedes (if any) */
+  GtkTreeViewDropPosition pos;
+  struct queue_entry *q;
+  GtkTreePath *path = ql_drop_path(w, GTK_TREE_MODEL(ql->store), x, y, &pos);
+  if(path) {
+    q = ql_path_to_q(GTK_TREE_MODEL(ql->store), path);
+    //fprintf(stderr, "  drop path: %s q=%p pos=%d\n",
+    //        gtk_tree_path_to_string(path), q, pos);
+  } else {
+    /* This generally means a drop past the end of the queue.  We find the last
+     * element in the queue and ask to move after that. */
+    for(q = ql->q; q && q->next; q = q->next)
+      ;
+    //fprintf(stderr, "  after end.  q=%p.  pos=%d\n", q, pos);
+  }
+  switch(pos) {
+  case GTK_TREE_VIEW_DROP_BEFORE:
+  case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
+    if(q) {
+      q = q->prev;
+      //fprintf(stderr, "  but we like to drop near %s\n",
+      //        q ? q->id : "NULL");
+    }
+    break;
+  default:
+    break;
+  }
+  /* Guarantee we never drop an empty list */
+  if(!tracks->nvec)
+    return;
+  /* Note that q->id can match one of ids[].  This doesn't matter for
+   * moveafter but TODO may matter for playlist support. */
+  switch(info_) {
+  case QUEUED_TRACKS_ID:
+  case PLAYLIST_TRACKS_ID:
+    /* Rearrangement within some widget.  Send ID and track data. */
+    ql->drop(ql, tracks->nvec, tracks->vec, ids->vec, q);
+    break;
+  case PLAYABLE_TRACKS_ID:
+    /* Copying between widgets.  IDs mean nothing so don't send them. */
+    ql->drop(ql, tracks->nvec, tracks->vec, NULL, q);
+    break;
+  }
+  if(path)
+    gtk_tree_path_free(path);
+}
+
+static int count_drag_targets(const GtkTargetEntry *targets) {
+  const GtkTargetEntry *t = targets;
+
+  while(t->target)
+    ++t;
+  return t - targets;
+}
+
 /** @brief Initialize a @ref queuelike */
 GtkWidget *init_queuelike(struct queuelike *ql) {
   D(("init_queuelike"));
@@ -429,7 +808,7 @@ GtkWidget *init_queuelike(struct queuelike *ql) {
       (ql->columns[n].name,
        r,
        "text", n,
-       "background", ql->ncolumns + BACKGROUND_COLUMN,
+       "cell-background", ql->ncolumns + BACKGROUND_COLUMN,
        "foreground", ql->ncolumns + FOREGROUND_COLUMN,
        (char *)0);
     gtk_tree_view_column_set_resizable(c, TRUE);
@@ -441,15 +820,70 @@ GtkWidget *init_queuelike(struct queuelike *ql) {
 
   /* The selection should support multiple things being selected */
   ql->selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ql->view));
+  g_object_ref(ql->selection);
   gtk_tree_selection_set_mode(ql->selection, GTK_SELECTION_MULTIPLE);
 
   /* Catch button presses */
   g_signal_connect(ql->view, "button-press-event",
                    G_CALLBACK(ql_button_release), ql);
 
+  /* Drag+drop*/
+  if(ql->drop) {
+    /* Originally this was:
+     *
+     *   gtk_tree_view_set_reorderable(GTK_TREE_VIEW(ql->view), TRUE);
+     *
+     * However this has a two deficiencies:
+     *
+     *   1) Only one row can be dragged at once.  It would be nice
+     *      to be able to do bulk rearrangements since the server
+     *      can cope with that well.
+     *   2) Dragging between windows is not possible.  When playlist
+     *      support appears, it should be possible to drag tracks
+     *      from the choose tag into the playlist.
+     *
+     * At the time of writing neither of these problems are fully solved, the
+     * code as it stands is just a stepping stone in that direction.
+     */
+
+    /* This view will act as a drag source */
+    gtk_drag_source_set(ql->view,
+                        GDK_BUTTON1_MASK,
+                        ql->drag_source_targets,
+                        count_drag_targets(ql->drag_source_targets),
+                        ql->drag_dest_actions);
+    /* This view will act as a drag destination */
+    gtk_drag_dest_set(ql->view,
+                      GTK_DEST_DEFAULT_HIGHLIGHT|GTK_DEST_DEFAULT_DROP,
+                      ql->drag_dest_targets,
+                      count_drag_targets(ql->drag_dest_targets),
+                      ql->drag_dest_actions);
+    g_signal_connect(ql->view, "drag-motion",
+                     G_CALLBACK(ql_drag_motion), ql);
+    g_signal_connect(ql->view, "drag-leave",
+                     G_CALLBACK(ql_drag_leave), ql);
+    g_signal_connect(ql->view, "drag-data-get",
+                     G_CALLBACK(ql_drag_data_get), ql);
+    g_signal_connect(ql->view, "drag-data-received",
+                     G_CALLBACK(ql_drag_data_received), ql);
+    make_treeview_multidrag(ql->view, NULL);
+    // TODO playing track should be refused by predicate arg
+  } else {
+    /* For queues that cannot accept a drop we still accept a copy out */
+    gtk_drag_source_set(ql->view,
+                        GDK_BUTTON1_MASK,
+                        ql->drag_source_targets,
+                        count_drag_targets(ql->drag_source_targets),
+                        ql->drag_source_actions);
+    g_signal_connect(ql->view, "drag-data-get",
+                     G_CALLBACK(ql_drag_data_get), ql);
+    make_treeview_multidrag(ql->view, NULL);
+  }
+  
   /* TODO style? */
 
-  ql->init();
+  if(ql->init)
+    ql->init(ql);
 
   /* Update display text when lookups complete */
   event_register("lookups-completed", queue_lookups_completed, ql);
@@ -459,6 +893,31 @@ GtkWidget *init_queuelike(struct queuelike *ql) {
   return scrolled;
 }
 
+/** @brief Destroy a queuelike
+ * @param ql Queuelike to destroy
+ *
+ * Returns @p ql to its initial state.
+ */
+void destroy_queuelike(struct queuelike *ql) {
+  if(ql->store) {
+    g_object_unref(ql->store);
+    ql->store = NULL;
+  }
+  if(ql->view) {
+    gtk_object_destroy(GTK_OBJECT(ql->view));
+    ql->view = NULL;
+  }
+  if(ql->menu) {
+    gtk_object_destroy(GTK_OBJECT(ql->menu));
+    ql->menu = NULL;
+  }
+  if(ql->selection) {
+    g_object_unref(ql->selection);
+    ql->selection = NULL;
+  }
+  ql->q = NULL;
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index 4b31fe9..b774f22 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder
- * Copyright (C) 2006-2008 Richard Kettlewell
+ * Copyright (C) 2006-2009 Richard Kettlewell
  *
  * 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
@@ -55,7 +55,7 @@ struct queuelike {
   const char *name;
   
   /** @brief Initialization function */
-  void (*init)(void);
+  void (*init)(struct queuelike *ql);
 
   /** @brief Columns */
   const struct queue_column *columns;
@@ -90,9 +90,45 @@ struct queuelike {
 
   /** @brief Menu callbacks */
   struct tabtype tabtype;
+
+  /** @brief Drag-drop callback, or NULL for no drag+drop
+   * @param ql Owning queuelike
+   * @param ntracks Number of tracks to be dropped
+   * @param tracks List of track names
+   * @param ids List of track IDs
+   * @param after_me Drop after this or NULL to drop at head
+   *
+   * If the rearrangement is impossible then the displayed queue must be put
+   * back.
+   */
+  void (*drop)(struct queuelike *ql, int ntracks, char **tracks, char **ids,
+               struct queue_entry *after_me);
+
+  /** @brief Source target list */
+  const GtkTargetEntry *drag_source_targets;
+
+  /** @brief Drag source actions */
+  GdkDragAction drag_source_actions;
+  
+  /** @brief Destination target list */
+  const GtkTargetEntry *drag_dest_targets;
+
+  /** @brief Drag destination actions */
+  GdkDragAction drag_dest_actions;
+  
 };
 
 enum {
+  PLAYABLE_TRACKS_ID,
+  QUEUED_TRACKS_ID,
+  PLAYLIST_TRACKS_ID
+};
+
+#define PLAYABLE_TRACKS (char *)"text/x-disorder-playable-tracks"
+#define QUEUED_TRACKS (char *)"text/x-disorder-queued-tracks"
+#define PLAYLIST_TRACKS (char *)"text/x-disorder-playlist-tracks"
+
+enum {
   QUEUEPOINTER_COLUMN,
   FOREGROUND_COLUMN,
   BACKGROUND_COLUMN,
@@ -100,15 +136,8 @@ enum {
   EXTRA_COLUMNS
 };
 
-/* TODO probably need to set "horizontal-separator" to 0, but can't find any
- * coherent description of how to set style properties in isolation. */
-#define BG_PLAYING 0
-#define FG_PLAYING 0
-
-#ifndef BG_PLAYING
-# define BG_PLAYING "#e0ffe0"
-# define FG_PLAYING "black"
-#endif
+#define BG_PLAYING "#e0ffe0"
+#define FG_PLAYING "black"
 
 extern struct queuelike ql_queue;
 extern struct queuelike ql_recent;
@@ -141,6 +170,7 @@ gboolean ql_button_release(GtkWidget *widget,
                            GdkEventButton *event,
                            gpointer user_data);
 GtkWidget *init_queuelike(struct queuelike *ql);
+void destroy_queuelike(struct queuelike *ql);
 void ql_update_list_store(struct queuelike *ql) ;
 void ql_update_row(struct queue_entry *q,
                    GtkTreeIter *iter);
@@ -157,6 +187,8 @@ const char *column_length(const struct queue_entry *q,
 struct tabtype *ql_tabtype(struct queuelike *ql);
 struct queue_entry *ql_iter_to_q(GtkTreeModel *model,
                                  GtkTreeIter *iter);
+struct queue_entry *ql_path_to_q(GtkTreeModel *model,
+                                 GtkTreePath *path);
 
 #endif /* QUEUE_GENERIC_H */
 
index beb40f5..ebf8555 100644 (file)
@@ -71,7 +71,7 @@ void ql_properties_activate(GtkMenuItem attribute((unused)) *menuitem,
     gtk_tree_model_iter_next(GTK_TREE_MODEL(ql->store), iter);
   }
   if(v->nvec)
-    properties(v->nvec, (const char **)v->vec);
+    properties(v->nvec, (const char **)v->vec, ql->view);
 }
 
 /* Scratch */
index 6dac960..c495bd8 100644 (file)
@@ -72,7 +72,7 @@ static void queue_playing_changed(void) {
   ql_new_queue(&ql_queue, q);
   /* Tell anyone who cares */
   event_raise("queue-list-changed", q);
-  event_raise("playing-track-changed", q);
+  event_raise("playing-track-changed", playing_track);
 }
 
 /** @brief Update the queue itself */
@@ -97,7 +97,7 @@ static void playing_completed(void attribute((unused)) *v,
   }
   actual_playing_track = q;
   queue_playing_changed();
-  time(&last_playing);
+  xtime(&last_playing);
 }
 
 /** @brief Schedule an update to the queue
@@ -135,13 +135,29 @@ static gboolean playing_periodic(gpointer attribute((unused)) data) {
   /* If there's a track playing, update its row */
   if(playing_track)
     ql_update_row(playing_track, 0);
+  /* If the first (nonplaying) track starts in the past, update the queue to
+   * get new expected start times; but rate limit this checking.  (If we only
+   * do it once a minute then the rest of the queue can get out of date too
+   * easily.) */
+  struct queue_entry *q = ql_queue.q;
+  if(q) {
+    if(q == playing_track)
+      q = q->next;
+    if(q) {
+      time_t now;
+      time(&now);
+      if(q->expected / 15 < now / 15)
+        queue_changed(0,0,0);
+    }
+  }
   return TRUE;
 }
 
 /** @brief Called at startup */
-static void queue_init(void) {
+static void queue_init(struct queuelike attribute((unused)) *ql) {
   /* Arrange a callback whenever the playing state changes */ 
   event_register("playing-changed", playing_changed, 0);
+  event_register("playing-started", playing_changed, 0);
   /* We reget both playing track and queue at pause/resume so that start times
    * can be computed correctly */
   event_register("pause-changed", playing_changed, 0);
@@ -152,6 +168,59 @@ static void queue_init(void) {
   g_timeout_add(1000/*ms*/, playing_periodic, 0);
 }
 
+static void queue_drop_completed(void attribute((unused)) *v,
+                                 const char *err) {
+  if(err) {
+    popup_protocol_error(0, err);
+    return;
+  }
+  /* The log should tell us the queue changed so we do no more here */
+}
+
+/** @brief Called when drag+drop completes */
+static void queue_drop(struct queuelike attribute((unused)) *ql,
+                       int ntracks,
+                       char **tracks, char **ids,
+                       struct queue_entry *after_me) {
+  int n;
+
+  if(ids) {
+    /* Rearrangement */
+    if(playing_track) {
+      /* If there's a playing track then you can't drag it anywhere  */
+      for(n = 0; n < ntracks; ++n) {
+        if(!strcmp(playing_track->id, ids[n])) {
+          fprintf(stderr, "cannot drag playing track\n");
+          return;
+        }
+      }
+      /* You can't tell the server to drag after the playing track by ID, you
+       * have to send "". */
+      if(after_me == playing_track)
+        after_me = NULL;
+      /* If you try to drag before the playing track (i.e. after_me=NULL on
+       * input) then the effect is just to drag after it, although there's no
+       * longer code to explicitly implement this. */
+    }
+    /* Tell the server to move them.  The log will tell us about the change (if
+     * indeed it succeeds!), so no need to rearrange the model now. */
+    disorder_eclient_moveafter(client,
+                               after_me ? after_me->id : "",
+                               ntracks, (const char **)ids,
+                               queue_drop_completed, NULL);
+  } else {
+    /* You can't tell the server to insert after the playing track by ID, you
+     * have to send "". */
+    if(after_me == playing_track)
+      after_me = NULL;
+    /* Play the tracks */
+    disorder_eclient_playafter(client,
+                               after_me ? after_me->id : "",
+                               ntracks, (const char **)tracks,
+                               queue_drop_completed, NULL);
+  }
+}
+
 /** @brief Columns for the queue */
 static const struct queue_column queue_columns[] = {
   { "When",   column_when,     0,        COL_RIGHT },
@@ -164,12 +233,28 @@ static const struct queue_column queue_columns[] = {
 
 /** @brief Pop-up menu for queue */
 static struct menuitem queue_menuitems[] = {
-  { "Track properties", ql_properties_activate, ql_properties_sensitive, 0, 0 },
-  { "Select all tracks", ql_selectall_activate, ql_selectall_sensitive, 0, 0 },
-  { "Deselect all tracks", ql_selectnone_activate, ql_selectnone_sensitive, 0, 0 },
-  { "Scratch playing track", ql_scratch_activate, ql_scratch_sensitive, 0, 0 },
-  { "Remove track from queue", ql_remove_activate, ql_remove_sensitive, 0, 0 },
-  { "Adopt track", ql_adopt_activate, ql_adopt_sensitive, 0, 0 },
+  { "Track properties", GTK_STOCK_PROPERTIES, ql_properties_activate, ql_properties_sensitive, 0, 0 },
+  { "Select all tracks", GTK_STOCK_SELECT_ALL, ql_selectall_activate, ql_selectall_sensitive, 0, 0 },
+  { "Deselect all tracks", NULL, ql_selectnone_activate, ql_selectnone_sensitive, 0, 0 },
+  { "Scratch playing track", GTK_STOCK_STOP, ql_scratch_activate, ql_scratch_sensitive, 0, 0 },
+  { "Remove track from queue", GTK_STOCK_DELETE, ql_remove_activate, ql_remove_sensitive, 0, 0 },
+  { "Adopt track", NULL, ql_adopt_activate, ql_adopt_sensitive, 0, 0 },
+};
+
+static const GtkTargetEntry queue_targets[] = {
+  {
+    QUEUED_TRACKS,                      /* drag type */
+    GTK_TARGET_SAME_WIDGET,             /* rearrangement within a widget */
+    QUEUED_TRACKS_ID                    /* ID value */
+  },
+  {
+    PLAYABLE_TRACKS,                             /* drag type */
+    GTK_TARGET_SAME_APP|GTK_TARGET_OTHER_WIDGET, /* copying between widgets */
+    PLAYABLE_TRACKS_ID,                          /* ID value */
+  },
+  {
+    .target = NULL
+  }
 };
 
 struct queuelike ql_queue = {
@@ -178,161 +263,14 @@ struct queuelike ql_queue = {
   .columns = queue_columns,
   .ncolumns = sizeof queue_columns / sizeof *queue_columns,
   .menuitems = queue_menuitems,
-  .nmenuitems = sizeof queue_menuitems / sizeof *queue_menuitems
+  .nmenuitems = sizeof queue_menuitems / sizeof *queue_menuitems,
+  .drop = queue_drop,
+  .drag_source_targets = queue_targets,
+  .drag_source_actions = GDK_ACTION_MOVE|GDK_ACTION_COPY,
+  .drag_dest_targets = queue_targets,
+  .drag_dest_actions = GDK_ACTION_MOVE|GDK_ACTION_COPY,
 };
 
-/* Drag and drop has to be figured out experimentally, because it is not well
- * documented.
- *
- * First you get a row-inserted.  The path argument points to the destination
- * row but this will not yet have had its values set.  The source row is still
- * present.  AFAICT the iter argument points to the same place.
- *
- * Then you get a row-deleted.  The path argument identifies the row that was
- * deleted.  By this stage the row inserted above has acquired its values.
- *
- * A complication is that the deletion will move the inserted row.  For
- * instance, if you do a drag that moves row 1 down to after the track that was
- * formerly on row 9, in the row-inserted call it will show up as row 10, but
- * in the row-deleted call, row 1 will have been deleted thus making the
- * inserted row be row 9.
- *
- * So when we see the row-inserted we have no idea what track to move.
- * Therefore we stash it until we see a row-deleted.
- */
-
-/** @brief Target row for drag */
-static int queue_drag_target = -1;
-
-static void queue_move_completed(void attribute((unused)) *v,
-                                 const char *err) {
-  if(err) {
-    popup_protocol_error(0, err);
-    return;
-  }
-  /* The log should tell us the queue changed so we do no more here */
-}
-
-static void queue_row_deleted(GtkTreeModel *treemodel,
-                              GtkTreePath *path,
-                              gpointer attribute((unused)) user_data) {
-  if(!suppress_actions) {
-#if 0
-    char *ps = gtk_tree_path_to_string(path);
-    fprintf(stderr, "row-deleted path=%s queue_drag_target=%d\n",
-            ps, queue_drag_target);
-    GtkTreeIter j[1];
-    gboolean jt = gtk_tree_model_get_iter_first(treemodel, j);
-    int row = 0;
-    while(jt) {
-      struct queue_entry *q = ql_iter_to_q(treemodel, j);
-      fprintf(stderr, " %2d %s\n", row++, q ? q->track : "(no q)");
-      jt = gtk_tree_model_iter_next(GTK_TREE_MODEL(ql_queue.store), j);
-    }
-    g_free(ps);
-#endif
-    if(queue_drag_target < 0) {
-      error(0, "unsuppressed row-deleted with no row-inserted");
-      return;
-    }
-    int drag_source = gtk_tree_path_get_indices(path)[0];
-
-    /* If the drag is downwards (=towards higher row numbers) then the target
-     * will have been moved upwards (=towards lower row numbers) by one row. */
-    if(drag_source < queue_drag_target)
-      --queue_drag_target;
-    
-    /* Find the track to move */
-    GtkTreeIter src[1];
-    gboolean srcv = gtk_tree_model_iter_nth_child(treemodel, src, NULL,
-                                                  queue_drag_target);
-    if(!srcv) {
-      error(0, "cannot get iterator to drag target %d", queue_drag_target);
-      queue_playing_changed();
-      queue_drag_target = -1;
-      return;
-    }
-    struct queue_entry *srcq = ql_iter_to_q(treemodel, src);
-    assert(srcq);
-    //fprintf(stderr, "move %s %s\n", srcq->id, srcq->track);
-    
-    /* Don't allow the currently playing track to be moved.  As above, we put
-     * the queue back into the right order straight away. */
-    if(srcq == playing_track) {
-      //fprintf(stderr, "cannot move currently playing track\n");
-      queue_playing_changed();
-      queue_drag_target = -1;
-      return;
-    }
-
-    /* Find the destination */
-    struct queue_entry *dstq;
-    if(queue_drag_target) {
-      GtkTreeIter dst[1];
-      gboolean dstv = gtk_tree_model_iter_nth_child(treemodel, dst, NULL,
-                                                    queue_drag_target - 1);
-      if(!dstv) {
-        error(0, "cannot get iterator to drag target predecessor %d",
-              queue_drag_target - 1);
-        queue_playing_changed();
-        queue_drag_target = -1;
-        return;
-      }
-      dstq = ql_iter_to_q(treemodel, dst);
-      assert(dstq);
-      if(dstq == playing_track)
-        dstq = 0;
-    } else
-      dstq = 0;
-    /* NB if the user attempts to move a queued track before the currently
-     * playing track we assume they just missed a bit, and put it after. */
-    //fprintf(stderr, " target %s %s\n", dstq ? dstq->id : "(none)", dstq ? dstq->track : "(none)");
-    /* Now we know what is to be moved.  We need to know the preceding queue
-     * entry so we can move it. */
-    disorder_eclient_moveafter(client,
-                               dstq ? dstq->id : "",
-                               1, &srcq->id,
-                               queue_move_completed, NULL);
-    queue_drag_target = -1;
-  }
-}
-
-static void queue_row_inserted(GtkTreeModel attribute((unused)) *treemodel,
-                               GtkTreePath *path,
-                               GtkTreeIter attribute((unused)) *iter,
-                               gpointer attribute((unused)) user_data) {
-  if(!suppress_actions) {
-#if 0
-    char *ps = gtk_tree_path_to_string(path);
-    GtkTreeIter piter[1];
-    gboolean pi = gtk_tree_model_get_iter(treemodel, piter, path);
-    struct queue_entry *pq = pi ? ql_iter_to_q(treemodel, piter) : 0;
-    struct queue_entry *iq = ql_iter_to_q(treemodel, iter);
-
-    fprintf(stderr, "row-inserted path=%s pi=%d pq=%p path=%s iq=%p iter=%s\n",
-            ps,
-            pi,
-            pq,
-            (pi
-             ? (pq ? pq->track : "(pq=0)")
-             : "(pi=FALSE)"),
-            iq,
-            iq ? iq->track : "(iq=0)");
-
-    GtkTreeIter j[1];
-    gboolean jt = gtk_tree_model_get_iter_first(treemodel, j);
-    int row = 0;
-    while(jt) {
-      struct queue_entry *q = ql_iter_to_q(treemodel, j);
-      fprintf(stderr, " %2d %s\n", row++, q ? q->track : "(no q)");
-      jt = gtk_tree_model_iter_next(GTK_TREE_MODEL(ql_queue.store), j);
-    }
-    g_free(ps);
-#endif
-    queue_drag_target = gtk_tree_path_get_indices(path)[0];
-  }
-}
-
 /** @brief Called when a key is pressed in the queue tree view */
 static gboolean queue_key_press(GtkWidget attribute((unused)) *widget,
                                 GdkEventKey *event,
@@ -353,14 +291,6 @@ static gboolean queue_key_press(GtkWidget attribute((unused)) *widget,
 GtkWidget *queue_widget(void) {
   GtkWidget *const w = init_queuelike(&ql_queue);
 
-  /* Enable drag+drop */
-  gtk_tree_view_set_reorderable(GTK_TREE_VIEW(ql_queue.view), TRUE);
-  g_signal_connect(ql_queue.store,
-                   "row-inserted",
-                   G_CALLBACK(queue_row_inserted), &ql_queue);
-  g_signal_connect(ql_queue.store,
-                   "row-deleted",
-                   G_CALLBACK(queue_row_deleted), &ql_queue);
   /* Catch keypresses */
   g_signal_connect(ql_queue.view, "key-press-event",
                    G_CALLBACK(queue_key_press), &ql_queue);
@@ -380,6 +310,45 @@ int queued(const char *track) {
   return 0;
 }
 
+/* Playing widget for mini-mode */
+
+static void queue_set_playing_widget(const char attribute((unused)) *event,
+                                     void attribute((unused)) *eventdata,
+                                     void *callbackdata) {
+  GtkLabel *w = callbackdata;
+
+  if(playing_track) {
+    const char *artist = namepart(playing_track->track, "display", "artist");
+    const char *album = namepart(playing_track->track, "display", "album");
+    const char *title = namepart(playing_track->track, "display", "title");
+    const char *ldata = column_length(playing_track, NULL);
+    if(!ldata)
+      ldata = "";
+    char *text;
+    byte_xasprintf(&text, "%s/%s/%s %s", artist, album, title, ldata);
+    gtk_label_set_text(w, text);
+  } else
+    gtk_label_set_text(w, "");
+}
+
+GtkWidget *playing_widget(void) {
+  GtkWidget *w = gtk_label_new("");
+  gtk_misc_set_alignment(GTK_MISC(w), 1.0, 0);
+  /* Spot changes to the playing track */
+  event_register("playing-track-changed",
+                 queue_set_playing_widget,
+                 w);
+  /* Use the best-known name for it */
+  event_register("lookups-complete",
+                 queue_set_playing_widget,
+                 w);
+  /* Keep the amount played so far up to date */
+  event_register("periodic-fast",
+                 queue_set_playing_widget,
+                 w);
+  return frame_widget(w, NULL);
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index 0923a6d..510aac9 100644 (file)
@@ -61,7 +61,7 @@ static void recent_changed(const char attribute((unused)) *event,
 }
 
 /** @brief Called at startup */
-static void recent_init(void) {
+static void recent_init(struct queuelike attribute((unused)) *ql) {
   /* Whenever the recent list changes on the server, re-fetch it */
   event_register("recent-changed", recent_changed, 0);
 }
@@ -78,10 +78,10 @@ static const struct queue_column recent_columns[] = {
 
 /** @brief Pop-up menu for recently played list */
 static struct menuitem recent_menuitems[] = {
-  { "Track properties", ql_properties_activate, ql_properties_sensitive,0, 0 },
-  { "Play track", ql_play_activate, ql_play_sensitive, 0, 0 },
-  { "Select all tracks", ql_selectall_activate, ql_selectall_sensitive, 0, 0 },
-  { "Deselect all tracks", ql_selectnone_activate, ql_selectnone_sensitive, 0, 0 },
+  { "Track properties", GTK_STOCK_PROPERTIES, ql_properties_activate, ql_properties_sensitive,0, 0 },
+  { "Play track", GTK_STOCK_MEDIA_PLAY, ql_play_activate, ql_play_sensitive, 0, 0 },
+  { "Select all tracks", GTK_STOCK_SELECT_ALL, ql_selectall_activate, ql_selectall_sensitive, 0, 0 },
+  { "Deselect all tracks", NULL, ql_selectnone_activate, ql_selectnone_sensitive, 0, 0 },
 };
 
 struct queuelike ql_recent = {
@@ -91,6 +91,8 @@ struct queuelike ql_recent = {
   .ncolumns = sizeof recent_columns / sizeof *recent_columns,
   .menuitems = recent_menuitems,
   .nmenuitems = sizeof recent_menuitems / sizeof *recent_menuitems,
+  .drag_source_targets = choose_targets,
+  .drag_source_actions = GDK_ACTION_COPY,
 };
 
 GtkWidget *recent_widget(void) {
index f5f91f0..cbedb18 100644 (file)
@@ -107,30 +107,30 @@ void start_rtp(void) {
   /* double-fork so we don't have to wait() later */
   if(!(pid = xfork())) {
     if(setsid() < 0)
-      fatal(errno, "error calling setsid");
+      disorder_fatal(errno, "error calling setsid");
     if(!(pid = xfork())) {
       /* grandchild */
       exitfn = _exit;
       /* log errors and output somewhere reasonably sane.  rtp_running()
        * will have made sure the directory exists. */
       if((fd = open(rtp_log, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0)
-       fatal(errno, "creating %s", rtp_log);
+       disorder_fatal(errno, "creating %s", rtp_log);
       if(dup2(fd, 1) < 0
         || dup2(fd, 2) < 0)
-       fatal(errno, "dup2");
+       disorder_fatal(errno, "dup2");
       if(close(fd) < 0)
-       fatal(errno, "close");
+       disorder_fatal(errno, "close");
       /* We don't want to hang onto whatever stdin was */
       if((fd = open("/dev/null", O_RDONLY)) < 0)
-        fatal(errno, "opening /dev/null");
+        disorder_fatal(errno, "opening /dev/null");
       if(dup2(fd, 0) < 0)
-        fatal(errno, "dup2");
+        disorder_fatal(errno, "dup2");
       if(close(fd) < 0)
-       fatal(errno, "close");
+       disorder_fatal(errno, "close");
       /* execute the player */
       execlp("disorder-playrtp",
             "disorder-playrtp", "--socket", rtp_socket, (char *)0);
-      fatal(errno, "disorder-playrtp");
+      disorder_fatal(errno, "disorder-playrtp");
     } else {
       /* child */
       _exit(0);
index 71b953d..2c4ffeb 100644 (file)
@@ -219,25 +219,25 @@ void load_settings(void) {
       if(!strcmp(vec[0], "color")) {
         GdkColor *color;
         if(nvec != 7) {
-          error(0, "%s: malformed '%s' command", path, vec[0]);
+          disorder_error(0, "%s: malformed '%s' command", path, vec[0]);
           continue;
         }
         for(n = 0; n < NSTYLES && strcmp(styles[n].name, vec[1]); ++n)
           ;
         if(n >= NSTYLES) {
-          error(0, "%s: unknown style '%s'", path, vec[1]);
+          disorder_error(0, "%s: unknown style '%s'", path, vec[1]);
           continue;
         }
         for(m = 0; m < NSTATES && strcmp(states[m], vec[2]); ++m)
           ;
         if(m >= NSTATES) {
-          error(0, "%s: unknown state '%s'", path, vec[2]);
+          disorder_error(0, "%s: unknown state '%s'", path, vec[2]);
           continue;
         }
         for(c = 0; c < NCOLORS && strcmp(colors[c].name, vec[3]); ++c)
           ;
         if(c >= NCOLORS) {
-          error(0, "%s: unknown color '%s'", path, vec[3]);
+          disorder_error(0, "%s: unknown color '%s'", path, vec[3]);
           continue;
         }
         color = (GdkColor *)((char *)styles[n].style + colors[c].offset) + m;
@@ -246,13 +246,13 @@ void load_settings(void) {
         color->blue = strtoul(vec[6], 0, 0);
       } else if(!strcmp(vec[0], "browser")) {
         if(nvec != 2) {
-          error(0, "%s: malformed '%s' command", path, vec[0]);
+          disorder_error(0, "%s: malformed '%s' command", path, vec[0]);
           continue;
         }
         browser = vec[1];
       } else
         /* mention errors but otherwise ignore them */
-        error(0, "%s: unknown command '%s'", path, vec[0]);
+        disorder_error(0, "%s: unknown command '%s'", path, vec[0]);
     }
     if(ferror(fp)) {
       fpopup_msg(GTK_MESSAGE_ERROR, "error reading %s: %s",
index 1d2a169..b078698 100644 (file)
@@ -660,13 +660,15 @@ static struct button users_buttons[] = {
     GTK_STOCK_ADD,
     users_add,
     "Create a new user",
-    0
+    0,
+    NULL,
   },
   {
     GTK_STOCK_REMOVE,
     users_delete,
     "Delete a user",
-    0
+    0,
+    NULL,
   },
 };
 #define NUSERS_BUTTONS (sizeof users_buttons / sizeof *users_buttons)
index eaad306..b3d8926 100644 (file)
@@ -35,19 +35,31 @@ SEDFILES=disorder.1 disorderd.8 disorder_config.5 \
 
 include ${top_srcdir}/scripts/sedfiles.make
 
-HTMLMAN=$(foreach man,$(man_MANS),$(man).html)
-$(HTMLMAN) : %.html : % $(top_srcdir)/scripts/htmlman
-       rm -f $@.new
-       $(top_srcdir)/scripts/htmlman $< >$@.new
-       chmod 444 $@.new
-       mv -f $@.new $@
+HTMLMAN=disorderd.8.html disorder.1.html disorder.3.html               \
+disorder_config.5.html disorder-dump.8.html disorder_protocol.5.html   \
+disorder-deadlock.8.html disorder-rescan.8.html disobedience.1.html    \
+disorderfm.1.html disorder-speaker.8.html disorder-playrtp.1.html      \
+disorder-normalize.8.html disorder-decode.8.html disorder-stats.8.html \
+disorder-dbupgrade.8.html disorder_templates.5.html                    \
+disorder_actions.5.html disorder_options.5.html disorder.cgi.8.html    \
+disorder_preferences.5.html disorder-choose.8.html
 
-TMPLMAN=$(foreach man,$(man_MANS),$(man).tmpl)
-$(TMPLMAN) : %.tmpl : % $(top_srcdir)/scripts/htmlman
-       rm -f $@.new
-       $(top_srcdir)/scripts/htmlman -stdhead $< >$@.new
-       chmod 444 $@.new
-       mv -f $@.new $@
+$(wordlist 2,9999,$(HTMLMAN)): disorderd.8.html
+disorderd.8.html: $(man_MANS)
+       $(top_srcdir)/scripts/htmlman -- $^
+
+TMPLMAN=disorderd.8.tmpl disorder.1.tmpl disorder.3.tmpl               \
+disorder_config.5.tmpl disorder-dump.8.tmpl disorder_protocol.5.tmpl   \
+disorder-deadlock.8.tmpl disorder-rescan.8.tmpl disobedience.1.tmpl    \
+disorderfm.1.tmpl disorder-speaker.8.tmpl disorder-playrtp.1.tmpl      \
+disorder-normalize.8.tmpl disorder-decode.8.tmpl disorder-stats.8.tmpl \
+disorder-dbupgrade.8.tmpl disorder_templates.5.tmpl                    \
+disorder_actions.5.tmpl disorder_options.5.tmpl disorder.cgi.8.tmpl    \
+disorder_preferences.5.tmpl disorder-choose.8.tmpl
+
+$(wordlist 2,9999,$(TMPLMAN)): disorderd.8.tmpl
+disorderd.8.tmpl: $(man_MANS)
+       $(top_srcdir)/scripts/htmlman -stdhead -extension tmpl -- $^
 
 disorder_templates.5.in: disorder_templates.5.head disorder_templates.5.tail \
                $(top_srcdir)/lib/macros-builtin.c \
index fa4bd42..4462237 100644 (file)
@@ -1,5 +1,5 @@
 .\"
-.\" Copyright (C) 2004-2008 Richard Kettlewell
+.\" Copyright (C) 2004-2009 Richard Kettlewell
 .\"
 .\" 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
@@ -23,334 +23,9 @@ disobedience \- GUI client for DisOrder jukebox
 .SH DESCRIPTION
 .B disobedience
 is a graphical client for DisOrder.
-.SH "WINDOWS AND ICONS"
-.SS "Server Menu"
-This has the following options:
-.TP
-.B Login
-Brings up the \fBLogin Details Window\fR; see below.
-.TP
-.B "Manage Users"
-Brings up the \fBUser Management Window\fR; see below.
-.TP
-.B Quit
-Terminates the program.
-.SS "Edit Menu"
-This has the following options:
-.TP
-.B "Select All Tracks"
-Select all tracks.
-.TP
-.B "Deselect All Tracks"
-Deselect all tracks.
-.TP
-.B Properties
-Edit the details of the selected tracks.
-See
-.B "Properties Window"
-below.
-.SS "Control Menu"
-This has the following options:
-.TP
-.B Scratch
-Interrupts the currently playing track.
-.TP
-.B Playing
-Pause and resume the current track.
-.TP
-.B "Random play"
-Enable and disable random play.
-Does not take effect until the currently playing track finishes.
-.TP
-.B "Network player"
-Enables or disables network play.
-See
-.B "NETWORK PLAY"
-below.
-.SS "Help Menu"
-This has only one option, "About DisOrder", which pops up a box giving the
-name, author and version number of the software.
-.SS "Controls"
-.TP
-.B "Pause button"
-The pause button can be used to pause and resume tracks.
-This button shows either a pause symbol (two vertical bars) or a resume symbol
-(a right-pointing arrow).
-.TP
-.B "Scratch button"
-The scratch button, a red cross, can be used to interrupt the currently playing
-track.
-.TP
-.B "Random play button"
-The random play button can be used to enable and disable random play.
-It does not take effect until the currently playing track finishes.
-When the button is green, random play is enabled.
-When it is grey, random play is disabled.
-.TP
-.B "Play button"
-The play button controls whether tracks will be played at all.
-As above it does not take effect until the currently playing track finishes.
-When the button is green, play is enabled.
-When it is grey, play is disabled.
-.TP
-.B "Network play button"
-The network play buttons enables or disables network play.
-See
-.B "NETWORK PLAY"
-below.
-When the button is green, network play is enabled.
-When it is grey, network play is disabled.
-.TP
-.B "Volume slider"
-The volume slider indicates the current volume level and can be used to adjust
-it.
-0 is silent and 10 is maximum volume.
-.TP
-.B "Balance slider"
-The balance slider indicates the current balance and can be used to adjust it.
--1 means only the left speaker, 0 means both speakers at equal volume and +1
-means the only the right speaker.
-.SS "Queue Tab"
-This displays the currently playing track and the queue.
-The currently playing track is at the top, and can be distinguished by
-the constantly updating timer.
-Queued tracks appear below it.
-.PP
-The left button can be use to select and deselect tracks.
-On its own it just selects the pointed track and deselects everything else.
-With CTRL it flips the state of the pointed track without affecting anything
-else.
-With SHIFT it selects every track from the last click to the current position
-and deselects everything else.
-With both CTRL and SHIFT it selects everything from the last click to the
-current position without deselecting anything.
-.PP
-Tracks can be moved within the queue by dragging them to a new position with
-the left button.
 .PP
-The right button pops up a menu.
-This has the following options:
-.TP
-.B Properties
-Edit the details of the selected tracks.
-See
-.B "Properties Window"
-below.
-.TP
-.B "Select All Tracks"
-Select all tracks.
-.TP
-.B "Deselect All Tracks"
-Deselect all tracks.
-.TP
-.B Scratch
-Interrupt the currently playing track.
-(Note that this appears even if you right click over a queued track rather
-than the currently playing track.)
-.TP
-.B "Remove track from queue"
-Remove the selected tracks from the queue.
-.TP
-.B "Adopt track"
-Sets the submitter of a randomly picked track to you.
-.SS "Recent Tab"
-This displays recently played tracks, the most recent at the top.
-.PP
-The left button functions as above, except that drag-and-drop rearrangement
-is not possible.
-The right button pops up a menu with the following options:
-.TP
-.B Properties
-Edit the details of the selected tracks.
-See
-.B "Properties Window"
-below.
-.TP
-.B "Play track"
-Play the select track(s);
-.TP
-.B "Select All Tracks"
-Select all tracks.
-.TP
-.B "Deselect All Tracks"
-Deselect all tracks.
-.SS "Choose Tab"
-This displays all the tracks known to the server in a tree structure.
-.PP
-Directories are represented with an arrow to their left.
-This can be clicked to reveal or hide the contents of the directory.
-The top level "directories" break up tracks by their first letter.
-.PP
-Playable files are represented by their name.
-If they are playing or in the queue then a notes icon appears next to them.
-.PP
-Left clicking on a file will select it.
-As with the queue tab you can use SHIFT and CTRL to select multiple files.
-.PP
-The text box at the bottom is a search form.
-If you enter search terms here then tracks containing all those words will be
-highlighted.
-You can also limit the results to tracks with particular tags, by including
-\fBtag:\fITAG\fR for each tag.
-.PP
-To start a new search just edit the contents of the search box.
-The cancel button to its right clears the current search.
-The up and down arrows will scroll the window to make the previous or next
-search result visible.
-.PP
-Right clicking over a track will pop up a menu with the following options:
-.TP
-.B Play
-Play selected tracks.
-.TP
-.B Properties
-Edit properties of selected tracks.
-See
-.B "Properties Window"
-below.
-.PP
-A middle click on a track will add it to the queue.
-.PP
-Right clicking over a directory will pop up a menu with the following options:
-.TP
-.B "Play all tracks"
-Play all the tracks in the directory, in the order they appear on screen.
-.TP
-.B "Track properties"
-Edit properties of all tracks in the directory.
-.TP
-.B "Select children"
-Select all the tracks in the directory (and deselect everything else).
-.TP
-.B "Deselect all tracks"
-Deselect everything.
-.SS "Added Tab"
-This displays a list of tracks recently added to the server's database.
-The most recently added track is at the top.
-.PP
-Left clicking a track will select it.
-CTRL and SHIFT work as above to select muliple files.
-.PP
-Right clicking over a track will pop up a menu with the following options:
-.TP
-.B "Track properties"
-Edit properties of selected tracks.
-See
-.B "Properties Window"
-below.
-.TP
-.B "Play track"
-Play selected tracks.
-.TP
-.B "Select All Tracks"
-Select all tracks.
-.TP
-.B "Deselect All Tracks"
-Deselect all tracks.
-.SS "Login Details Window"
-The login details window allows you to edit the connection details and
-authorization information used by Disobedience.
-.PP
-At the top is a 'remote' switch.
-If this is enabled then you can use the \fBHostname\fR and \fBService\fR
-fields to connect to a remote server.
-If it is disabled then then Disobedience will connect to a local server
-instead.
-.PP
-Below this are four text entry fields:
-.TP
-.B Hostname
-The host to connect to.
-.TP
-.B Service
-The service name or port number to connect to.
-.TP
-.B "User name"
-The user name to log in as.
-.TP
-.B Password
-The password to use when logging in.
-Note that this is NOT your login password but is your password to the
-DisOrder server.
-.PP
-It has two buttons:
-.TP
-.B Login
-This button attempts to (re-)connect to the server with the currently displayed
-settings.
-The settings are saved in
-.IR $HOME/.disorder/passwd .
-on success.
-.TP
-.B Close
-This button closes the window, discarding any unsaved changes.
-.SS "Properties Window"
-This window contains details of one or more tracks and allows them to be
-edited.
-.PP
-The Artist, Album and Title fields determine how the tracks appear in
-the queue and recently played tabs.
-.PP
-The Tags field determine which tags apply to the track.
-Tags are separated by commas and can contain any printing characters except
-comma.
-.PP
-The Weight field determines the track weight.  Tracks with higher weights are
-proportionately more likely to be picked at random.  The default weight is
-90000, and the maximum weight is 2147483647.
-.PP
-The Random checkbox determines whether the track will be picked at random.
-Random play is enabled for every track by default, but it can be turned off
-here.
-.PP
-The double-headed arrow to the right of each preference will propagate its
-value to all the other tracks in the window.
-For instance, this can be used to efficiently correct the artist or album
-fields, or bulk-disable random play for many tracks.
-.PP
-Press "OK" to confirm all changes and close the window, "Apply" to confirm
-changes but keep the window open and "Cancel" to close the window and discard
-all changes.
-.SS "User Management Window"
-This window is primarily of interest to adminstrators, and will not be
-available to users without admin rights.  The left hand side is a list of all
-users; the right hand side contains the editable details of the currently
-selected user.
-.PP
-When you select any user you can edit their email address or change their
-password.  It is also possible to edit the individual user rights.  Click on
-the "Apply" button to commit any changes you make.
-.PP
-The "Add" button creates a new user.  You must enter at least a username.
-Default rights are setting according to local configuration, \fInot\fR server
-configuration (but this may be changed in the future).  As above, click on
-"Apply" to actually create the new user.
-.PP
-The "Delete" button deletes the selected user.  This operation cannot be
-undone.
-.SH "KEYBOARD SHORTCUTS"
-.TP
-.B CTRL+A
-Select all tracks (queue/recent)
-.TP
-.B CTRL+L
-Brings up the \fBLogin Details Window\fR.
-.TP
-.B CTRL+Q
-Quit.
-.SH "NETWORK PLAY"
-Network play uses a background
-.BR disorder\-playrtp (1)
-process.
-If you quit Disobedience the player will continue playing and can be
-disabled from a later run of Disobedience.
-.PP
-The player will log to
-.I ~/.disorder/HOSTNAME\-rtp.log
-so look there if it does not seem to be working.
-.PP
-You can stop it without running Disobedience by the command
-.BR "killall disorder\-playrtp" .
+Please refer to Disobedience's HTML manual for further information.  This can
+be found at dochtmldir/index.html.
 .SH OPTIONS
 .TP
 .B \-\-config \fIPATH\fR, \fB\-c \fIPATH
@@ -380,41 +55,6 @@ The screen number to use.
 .\" .TP
 .\" .B \-\-sync
 .\" Make all X requests synchronously.
-.SH CONFIGURATION
-If you are using
-.B disobedience
-on the same host as the server then no additional configuration should be
-required.
-.PP
-If it is running on a different host then the easiest way to set it up is to
-use the login details window in Disobedience.
-Enter the connection details, use Login to connect to the server, and then
-use Save to store them for future sessions.
-.PP
-The other clients read their configuration from the same location so after
-setting up with Disobedience, tools such as
-.BR disorder (1)
-should work as well.
-.SH BUGS
-There is no particular provision for multiple users of the same computer
-sharing a single \fBdisorder\-playrtp\fR process.
-This shouldn't be too much of a problem in practice but something could
-perhaps be done given demand.
-.PP
-Try to do remote user management when the server is configured to refuse this
-produces rather horrible error behavior.
-.PP
-Only one track can be dragged at a time.
-.PP
-Resizing columns doesn't work very well.
-This is a GTK+ bug.
-.SH FILES
-.TP
-.I ~/.disorder/HOSTNAME\-rtp
-Socket for communication with RTP player.
-.TP
-.I ~/.disorder/HOSTNAME\-rtp.log
-Log file for RTP player.
 .SH "SEE ALSO"
 .BR disorder\-playrtp (1),
 .BR disorder_config (5)
index 5e0d091..c92c55a 100644 (file)
@@ -1,5 +1,5 @@
 .\"
-.\" Copyright (C) 2007, 2008 Richard Kettlewell
+.\" Copyright (C) 2007-2009 Richard Kettlewell
 .\"
 .\" 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
@@ -21,38 +21,66 @@ disorder-playrtp \- play DisOrder network broadcasts
 .B disorder\-playrtp
 .RI [ OPTIONS ]
 .RB [ \-\- ]
-.RI [[ GROUP ]
+.RI [[ ADDRESS ]
 .IR PORT ]
 .SH DESCRIPTION
 \fBdisorder\-playrtp\fR plays a network broadcast sent from the specified
 address.
 .PP
-If neither a group nor port are specified then the local DisOrder
+If neither an address nor port are specified then the local DisOrder
 configuration is consulted to find the server and the server is asked where the
 RTP stream is.
 .PP
 If just a port is specified then the RTP stream is assumed to be unicast or
 broadcast to that port.
 .PP
-If a group and a port are specified then the RTP stream is assumed to be
-multicast to that group and port.
+If an address and a port are specified then the RTP stream is assumed to be
+multicast to that group address and port.
 .SH OPTIONS
 The default sound API is the first of the ones listed below that are available.
 Usually this implies ALSA under Linux and Core Audio under OS X.
 .TP
-.B \-\-alsa\fR, \fB-\a
+.B \-\-alsa\fR, \fB\-a
 Use ALSA to play sound.
+Only available on Linux.
 .TP
 .B \-\-oss\fR, \fB\-o
 Use OSS to play sound.
+Only available on Linux and FreeBSD.
 .TP
 .B \-\-core\-audio\fR, \fB\-c
 Use Core Audio to play sound.
+Only available on Macs.
 .TP
 .B \-\-device \fIDEVICE\fR, \fB\-D \fIDEVICE\fR
 Specifies the audio device to use.
-The exact meaning of this is platform-dependent; on Linux it is the
-ALSA device name.
+See
+.B "DEVICE NAMES"
+below for more information.
+.TP
+.B \-\-command \fICOMMAND\fR, \fB-e \fICOMMAND\fR
+Instead of sending to a physical audio device, invoke \fICOMMAND\fR using the
+shell and write audio samples to its standard input.
+Currently the input will be 44100KHz 16-bit signed stereo samples.
+If \fICOMMAND\fR exits it is re-executed; any samples that had been written to
+the pipe but not processed by the previous instance will be lost.
+.IP
+.B \-\-device
+is redundant with this option, but you might want to set
+.BR \-\-pause\-mode .
+.IP
+As an example,
+.B "-e \(aqcat > dump\(aq"
+would log audio data to a file for later processing.
+You could convert it to another format with, for instance:
+.IP
+.B "sox -c2 -traw -r44100 -s -w dump dump.wav"
+.TP
+.B \-\-pause\-mode \fIMODE\fR, \fB-P \fIMODE
+Set the pause mode for \fB\-\-command\fR to either \fBsilence\fR (the default), in
+which pauses are represented by sending silent samples, or \fBsuspend\fR, in which
+writes to  the subprocess are suspended, requiring it to infer a pause from flow
+control.
 .TP
 .B \-\-config \fIPATH\fR, \fB\-C \fIPATH
 Set the configuration file.
@@ -70,26 +98,30 @@ Display a usage message.
 Display version number.
 .SS "Buffer Control Options"
 You shouldn't need to use these options.
+Their effects are subject to change between version without warning.
+You should consult the source code for details of their effects.
 .TP
 .B \-\-min \fIFRAMES\fR, \fB\-m \fIFRAMES\fR
 Specifies the buffer low watermark in frames.
-If the number of frames falls below this value then playing will be
-stopped until the buffer fills up.
-.TP
-.B \-\-buffer \fIFRAMES\fR, \fB\-b \fIFRAMES\fR
-Specifies the buffer high watermark in frames.
-Once there are this many frames in the buffer, playing will be (re-)started.
+This also acts as the target buffer occupancy.
 .TP
 .B \-\-max \fIFRAMES\fR, \fB\-x \fIFRAMES\fR
 Specifies the maximum buffer size in frames.
 If there are this many frames in the buffer then reading from the
 network socket will be suspended.
-The default is four times the \fB\-\-buffer\fR value.
+The default is twice the \fB\-\-min\fR value.
 .TP
 .B \-\-rcvbuf \fIBYTES\fR, \fB\-R \fIBYTES\fR
 Specifies socket receive buffer size.
-The default is 131072 (128Kbytes).
+The default is not to change the buffer size, i.e. you get whatever the
+local operating system chooses.
 The buffer size will not be reduced below the operating system's default.
+.TP
+.B \-\-monitor\fR, \fB\-M
+Periodically report how close to the buffer low watermark the buffer is.
+If you have trouble with poor playback quality, enable this option to see if
+the buffer is emptying out (or overfilling, though there are measures to
+prevent that from happening).
 .SH "REMOTE CONTROL"
 The
 .B \-\-socket
@@ -113,6 +145,16 @@ After the first command the connection is closed.
 Only one connection at a time will be serviced.
 .PP
 This protocol is not guaranteed to be stable.
+.SH "DEVICE NAMES"
+.SS "Core Audio"
+On a Mac, the device name can either be the human-readable name of the desired
+output or its UID.
+To get a list of the human-readable names, visit System Preferences -> Sound;
+the Type column has the name you want.
+.PP
+For example, you might use "Built-in Output" for the built-in speaker
+or "Built-in Line Output" if you have connected external speakers.
+Remember to quote the name.
 .SH "SEE ALSO"
 .BR disobedience (1),
 .BR disorder_config (5),
index 784083c..b61a825 100644 (file)
@@ -152,6 +152,23 @@ Add \fITRACKS\fR to the end of the queue.
 .B playing
 Report the currently playing track.
 .TP
+.B playlist-del \fIPLAYLIST\fR
+Deletes playlist \fIPLAYLIST\fR.
+.TP
+.B playlist-get \fIPLAYLIST\fR
+Gets the contents of playlist \fIPLAYLIST\fR.
+.TP
+.B playlist-set \fIPLAYLIST\fR [\fIPATH\fR]
+Set the contents of playlist \fIPLAYLIST\fR.
+If an absolute path name is specified then the track list is read from
+that filename.
+Otherwise the track list is read from standard input.
+In either case, the list is terminated either by end of file or by a line
+containing a single ".".
+.TP
+.B playlists
+Lists known playlists (in no particular order).
+.TP
 .B prefs \fITRACK\fR
 Display all the preferences for \fITRACK\fR.
 See \fBdisorder_preferences\fR (5).
@@ -173,6 +190,11 @@ recently played one.
 .TP
 .B reconfigure
 Make the daemon reload its configuration file.
+.IP
+Not all configuration options can be modified during the lifetime of the
+server; of those that can't, some will just be ignored if they change while
+others will cause the new configuration to be rejected.
+See \fBdisorder_config\fR(5) for details.
 .TP
 .B remove \fITRACK\fR
 Remove a track from the queue.
index 5bd10b3..cbd2278 100644 (file)
@@ -146,7 +146,7 @@ If the track or key are not found a null pointer is returned.
 .IP
 This function sets the value of \fBkey\fR for \fBtrack\fR to
 \fBvalue\fR.
-On success, 0 is returned; on error, -1 is returned.
+On success, 0 is returned; on error, \-1 is returned.
 .IP
 If \fBvalue\fR is a null pointer then the preference is deleted.
 .IP
@@ -292,7 +292,7 @@ match them back up to the right collection to call
 .fi
 .IP
 Check whether file \fBpath\fR under \fBroot\fR still exists.
-Should return 1 if it exists, 0 if it does not and -1 on error.
+Should return 1 if it exists, 0 if it does not and \-1 on error.
 This is run in the main server process.
 .PP
 Both scan and recheck are executed inside a subprocess, so it will not
@@ -318,8 +318,7 @@ A standalone player that writes directly to some suitable audio
 device.
 .TP
 .B DISORDER_PLAYER_RAW
-A player that writes raw samples to \fB$DISORDER_RAW_FD\fR, for
-instance by using the \fBdisorder\fR libao driver.
+A player that writes raw samples to \fB$DISORDER_RAW_FD\fR.
 .RE
 .IP
 Known capabilities are:
@@ -392,10 +391,10 @@ This function must never block, as it runs inside the main loop of the
 server.
 .IP
 On success, should return 0 and set \fB*playedp\fR to the number of
-seconds played so far of this track, or to -1 if this cannot be
+seconds played so far of this track, or to \-1 if this cannot be
 determined.
 .IP
-On error, should return -1.
+On error, should return \-1.
 .PP
 .nf
 \fBvoid disorder_play_resume(void *data);
index e2f1ab0..a5b55ba 100644 (file)
@@ -1,5 +1,5 @@
 .\"
-.\" Copyright (C) 2004-2008 Richard Kettlewell
+.\" Copyright (C) 2004-2009 Richard Kettlewell
 .\"
 .\" 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
@@ -177,7 +177,7 @@ Backslash
 Quotation mark
 .\" "
 .TP
-.B \e'
+.B \e\(aq
 Apostrophe
 .TP
 .B \en
@@ -223,6 +223,8 @@ The home directory for state files.
 Defaults to
 .IR pkgstatedir .
 The server will create this directory on startup if it does not exist.
+.IP
+This setting cannot be changed during the lifetime of the server.
 .TP
 .B plugins \fIPATH\fR
 Adds a directory to the plugin path.
@@ -252,6 +254,8 @@ The pattern should not attempt to include the collection root, which is
 automatically included, but should include the proper extension.
 .IP
 The default is \fB{/artist}{/album}{/title}{ext}\fR.
+.IP
+This setting cannot be changed during the lifetime of the server.
 .TP
 .B api \fINAME\fR
 Selects the backend used to play sound and to set the volume.
@@ -275,13 +279,19 @@ Execute a command.
 This is the default if
 .B speaker_command
 is specified, or if no native is available.
+.IP
+You might want to set
+.B pause_mode
+with this backend.
 .TP
-.B network
+.B rtp
 Transmit audio over the network.
 This is the default if \fBbroadcast\fR is specified.
 You can use
 .BR disorder-playrtp (1)
 to receive and play the resulting stream on Linux and OS X.
+.B network
+is a deprecated synonym for this API.
 .RE
 .TP
 .B authorization_algorithm \fIALGORITHM\fR
@@ -291,14 +301,22 @@ See
 .BR disorder_protocol (5)
 for more details.
 .TP
-.B broadcast \fIADDRESS\fR \fIPORT\fR
+.B broadcast \fR[\fIFAMILY\fR] \fIADDRESS\fR \fIPORT\fR
 Transmit sound data to \fIADDRESS\fR using UDP port \fIPORT\fR.
-This implies \fBapi network\fR.
+This implies \fBapi rtp\fR.
+.IP
+\fIFAMILY\fR can be \fB-4\fR or \fB-6\fR to force IPv4 or IPv6, if this is not
+implied by \fIADDRESS\fR.
+Note that IPv6 is not currently well tested.
 .IP
 See also \fBmulticast_loop\fR and \fBmulticast_ttl\fR.
 .TP
-.B broadcast_from \fIADDRESS\fR \fIPORT\fR
+.B broadcast_from \fR[\fIFAMILY\fR] \fIADDRESS\fR \fIPORT\fR
 Sets the (local) source address used by \fBbroadcast\fR.
+.IP
+\fIFAMILY\fR can be \fB-4\fR or \fB-6\fR to force IPv4 or IPv6, if this is not
+implied by \fIADDRESS\fR.
+Note that IPv6 is not currently well tested.
 .TP
 .B channel \fICHANNEL\fR
 The mixer channel that the volume control should use.
@@ -307,6 +325,7 @@ For \fBapi oss\fR the possible values are:
 .RS
 .TP 8
 .B pcm
+
 Output level for the audio device.
 This is probably what you want and is the default.
 .TP
@@ -361,11 +380,17 @@ It must be an absolute path and should not end with a "/".
 .B cookie_key_lifetime \fISECONDS\fR
 Lifetime of the signing key used in constructing cookies.  The default is one
 week.
+.IP
+If this is changed during the lifetime of the server, the current key doesn't
+hvave its lifetime retroactively changed.
 .TP
 .B cookie_login_lifetime \fISECONDS\fR
 Lifetime of a cookie enforced by the server.  When the cookie expires the user
 will have to log in again even if their browser has remembered the cookie that
 long.  The default is one day.
+.IP
+If this is changed during the lifetime of the server, cookies that have already
+een generated don't hvave their lifetime retroactively changed.
 .TP
 .B default_rights \fIRIGHTS\fR
 Defines the set of rights given to new users.
@@ -386,34 +411,39 @@ will be tried.
 .IP
 For \fBapi alsa\fR this is the device name to use.
 .IP
-For \fBapi coreaudio\fR this is currently ignored.
+For \fBapi coreaudio\fR this can be either the UID or the human-readable
+name of the desired device.
+For a list of names, visit System Preferences -> Sound and look at the Type column.
+For example, you might use "Built-in Output" for the built-in speaker
+or "Built-in Line Output" if you have connected external speakers.
+Remember to quote the name.
 .IP
 The default is \fBdefault\fR, which is intended to map to whatever the system's
 default is.
 .TP
 .B gap \fISECONDS\fR
-Specifies the number of seconds to leave between tracks.
-The default is 0.
-.IP
-NB this option currently DOES NOT WORK.  If there is genuine demand it might be
-reinstated.
+This option no longer does anything and will be removed in a future version.
 .TP
 .B history \fIINTEGER\fR
 Specifies the number of recently played tracks to remember (including
 failed tracks and scratches).
+.IP
+If this is changed during the lifetime of the server, it won't actually reduce
+the size of the list until it is next modified.
 .TP
-.B listen \fR[\fIHOST\fR] \fISERVICE\fR
+.B listen \fR[\fIFAMILY\fR] \fR[\fIHOST\fR] \fISERVICE\fR
 Listen for connections on the address specified by \fIHOST\fR and port
 specified by \fISERVICE\fR.
-If \fIHOST\fR is omitted then listens on all local addresses.
+If \fIHOST\fR is omitted, or is \fB*\fR, then listens on all local addresses.
+.IP
+\fIFAMILY\fR can be \fB-4\fR or \fB-6\fR to force IPv4 or IPv6, if this is not
+implied by \fIHOST\fR.
+Note that IPv6 is not currently well tested.
 .IP
 Normally the server only listens on a UNIX domain socket.
 .TP
 .B lock yes\fR|\fBno
-Determines whether the server locks against concurrent operation.
-Default is \fByes\fR.
-There is no good reason to set this to \fBno\fR and the option will
-probably be removed in a future version.
+This option no longer does anything and will be removed in a future version.
 .TP
 .B mixer \fIDEVICE\fR
 The mixer device name, if it needs to be specified separately from
@@ -430,12 +460,12 @@ For \fBapi coreaudio\fR, volume setting is not currently supported.
 .B multicast_loop yes\fR|\fBno
 Determines whether multicast packets are loop backed to the sending host.
 The default is \fByes\fR.
-This only applies if \fBapi\fR is set to \fBnetwork\fR and \fBbroadcast\fR
+This only applies if \fBapi\fR is set to \fBrtp\fR and \fBbroadcast\fR
 is actually a multicast address.
 .TP
 .B multicast_ttl \fIHOPS\fR
 Set the maximum number of hops to send multicast packets.
-This only applies if \fBapi\fR is set to \fBnetwork\fR and
+This only applies if \fBapi\fR is set to \fBrtp\fR and
 \fBbroadcast\fR is actually a multicast address.
 The default is 1.
 .TP
@@ -474,15 +504,23 @@ namepart album  "/([^/]+)/[^/]+$"                          $1 *
 namepart artist "/([^/]+)/[^/]+/[^/]+$"                    $1 *
 namepart ext    "(\\.[a-zA-Z0-9]+)$"                        $1 *
 .fi
+.IP
+This setting cannot be changed during the lifetime of the server.
 .TP
 .B new_bias \fIWEIGHT\fR
 The weight for new tracks.
 The default is 450000, i.e. recently added tracks are a fifty times as likely
 to be picked as normal.
+.IP
+New values of this option may be picked up from the configuration file even
+without a reload.
 .TP
 .B new_bias_age \fISECONDS\fR
 The maximum age of tracks that \fBnew_bias\fR applies to, in seconds.
 The default is one week.
+.IP
+New values of this option may be picked up from the configuration file even
+without a reload.
 .TP
 .B new_max \fIMAX\fR
 The maximum number of tracks to list when reporting newly noticed tracks.
@@ -504,6 +542,8 @@ by programs it executes.
 If you have limited CPU then it might help to set this to a small
 negative value.
 The default is 0.
+.IP
+Changes to this value during the lifetime of the server are ignored.
 .TP
 .B nice_speaker \fIPRIORITY\fR
 Set the speaker process priority.
@@ -514,12 +554,27 @@ standards but depends on reasonably timely scheduling.
 If you have limited CPU then it might help to set this to a small
 negative value.
 The default is 0.
+.IP
+Changes to this value during the lifetime of the server are ignored.
 .TP
 .B noticed_history
 The maximum days that a track can survive in the database of newly added
 tracks.
 The default is 31.
 .TP
+.B pause_mode \fIMODE
+Sets the pause mode for the \fBcommand\fR backend.
+The possible values are:
+.RS
+.TP
+.B silence
+Send silent (0-value) samples when paused.
+This is the default.
+.TP
+.B suspend
+Stop writing when paused.
+.RE
+.TP
 .B player \fIPATTERN\fR \fIMODULE\fR [\fIOPTIONS.. [\fB\-\-\fR]] \fIARGS\fR...
 Specifies the player for files matching the glob \fIPATTERN\fR.
 \fIMODULE\fR specifies which plugin module to use.
@@ -528,6 +583,10 @@ The following options are supported:
 .RS
 .TP
 .B \-\-wait\-for\-device\fR[\fB=\fIDEVICE\fR]
+\fBThis option is deprecated\fR.
+If you want gapless play use raw-format players and the speaker process instead.
+(This the default in any case.)
+.IP
 Waits (for up to a couple of seconds) for the default, or specified, libao
 device to become openable.
 .TP
@@ -550,9 +609,6 @@ Identical to the \fBexec\fR except that the player is expected to use the
 DisOrder raw player protocol.
 .BR disorder-decode (8)
 can decode several common audio file formats to this format.
-If your favourite format is not supported, but you have a player
-which uses libao, there is also a libao driver which supports this format;
-see below for more information about this.
 .TP
 .B shell \fR[\fISHELL\fR] \fICOMMAND\fR
 The command is executed using the shell.
@@ -577,16 +633,24 @@ command.
 If
 .B player
 is used without arguments, the list of players is cleared.
+.IP
+Although players can be changed during the lifetime of the server, note that
+background decoders will not be stopped and restarted using changed
+configuration once they have been started.
 .TP
 .B prefsync \fISECONDS\fR
-The interval at which the preferences log file will be synchronised.
-Defaults to 3600, i.e. one hour.
+This option no longer does anything and will be removed in a future version.
 .TP
 .B queue_pad \fICOUNT\fR
 The target size of the queue.
 If random play is enabled then randomly picked tracks will be added until
 the queue is at least this big.
 The default is 10.
+.IP
+If this is reduced during the lifetime of the server, the queue won't be
+reduced in size to fit; it just won't start growing again until it is under the
+new value.
+However, if it is increased, new tracks will start being added immediately.
 .TP
 .B reminder_interval \fISECONDS\fR
 The minimum number of seconds that must elapse between password reminders.
@@ -601,6 +665,9 @@ The minimum number of seconds that must elapse after a track has been played
 before it can be picked at random.  The default is 8 hours.  If this is set to
 0 then there is no limit, though current \fBdisorder-choose\fR will not pick
 anything currently listed in the recently-played list.
+.IP
+New values of this option may be picked up from the configuration file even
+without a reload.
 .TP
 .B sample_format \fIBITS\fB/\fIRATE\fB/\fICHANNELS
 Describes the sample format expected by the \fBspeaker_command\fR (below).
@@ -623,7 +690,7 @@ The default is
 .BR 16/44100/2 .
 .PP
 With the
-.B network
+.B rtp
 backend the sample format is forced to
 .B 16b/44100/2
 and with the
@@ -688,6 +755,8 @@ cleared.
 .IP
 There is a default set of stopwords built in, but this option can be used to
 augment or replace that list.
+.IP
+This setting cannot be changed during the lifetime of the server.
 .TP
 .B tracklength \fIPATTERN\fR \fIMODULE\fR
 Specifies the module used to calculate the length of files matching
@@ -696,18 +765,29 @@ Specifies the module used to calculate the length of files matching
 .IP
 If \fBtracklength\fR is used without arguments then the list of modules is
 cleared.
+.IP
+Track lengths are cached in the database, and changing this setting won't cause
+them to be regenerated.
 .TP
 .B user \fIUSERNAME\fR
 Specifies the user to run as.
 Only makes sense if invoked as root (or the target user).
+.IP
+This setting cannot be changed during the lifetime of the server
+(and if it is changed with a restart, you will need to adjust file permissions
+on the server's database).
 .SS "Client Configuration"
 These options would normally be used in \fI~\fRUSERNAME\fI/.disorder/passwd\fR
 or
 \fIpkgconfdir/config.\fRUSERNAME.
 .TP
-.B connect \fIHOST SERVICE\fR
+.B connect \fR[\fIFAMILY\fR] \fIHOST SERVICE\fR
 Connect to the address specified by \fIHOST\fR and port specified by
 \fISERVICE\fR.
+.IP
+\fIFAMILY\fR can be \fB-4\fR or \fB-6\fR to force IPv4 or IPv6, if this is not
+implied by \fIHOST\fR.
+Note that IPv6 is not currently well tested.
 .TP
 .B password \fIPASSWORD\fR
 Specify password.
@@ -725,8 +805,14 @@ This must be set if you have online registration enabled.
 .TP
 .B refresh \fISECONDS\fR
 Specifies the maximum refresh period in seconds.
+The refresh period is the time after which the web interface's queue and manage
+pages will automatically reload themselves.
 Default 15.
 .TP
+.B refresh_min \fISECONDS\fR
+Specifies the minimum refresh period in seconds.
+Default 1.
+.TP
 .B sendmail \fIPATH\fR
 The path to the Sendmail executable.
 This must support the \fB-bs\fR option (Postfix, Exim and Sendmail should all
@@ -781,26 +867,6 @@ longer needs to be specified.
 .IP
 This must be the full URL, e.g. \fBhttp://myhost/cgi-bin/jukebox\fR and not
 \fB/cgi-bin/jukebox\fR.
-.SH "GLOBAL PREFERENCES"
-.SH "LIBAO DRIVER"
-.SS "Raw Protocol Players"
-Raw protocol players are expected to use the \fBdisorder\fR libao driver.
-Programs that use libao generally have command line options to select the
-driver and pass options to it.
-.SS "Driver Options"
-The known driver options are:
-.TP
-.B fd
-The file descriptor to write to.
-If this is not specified then the driver looks like the environment
-variable \fBDISORDER_RAW_FD\fR.
-If that is not set then the default is 1 (i.e. standard output).
-.TP
-.B fragile
-If this is set to a nonzero value then the driver will call \fB_exit\fR(2) if a
-write to the output file descriptor fails.
-This is a workaround for buggy players such as \fBogg123\fR that ignore
-write errors.
 .SH "REGEXP SUBSTITUTION RULES"
 Regexps are PCRE regexps, as defined in \fBpcrepattern\fR(3).
 The only option used is \fBPCRE_UTF8\fR.
index abd0caa..bb95ff0 100644 (file)
@@ -38,6 +38,15 @@ that comments are prohibited.
 Bodies borrow their syntax from RFC821; they consist of zero or more ordinary
 lines, with any initial full stop doubled up, and are terminated by a line
 consisting of a full stop and a line feed.
+.PP
+Commands only have a body if explicitly stated below.
+If they do have a body then the body should always be sent immediately;
+unlike (for instance) the SMTP "DATA" command there is no intermediate step
+where the server asks for the body to be sent.
+.PP
+Replies also only have a body if stated below.
+The presence of a reply body can always be inferred from the response code;
+if the last digit is a 3 then a body is present, otherwise it is not.
 .SH COMMANDS
 Commands always have a command name as the first field of the line; responses
 always have a 3-digit response code as the first field.
@@ -47,8 +56,6 @@ All commands require the connection to have been already authenticated unless
 stated otherwise.
 If not stated otherwise, the \fBread\fR right is sufficient to execute
 the command.
-.PP
-Neither commands nor responses have a body unless stated otherwise.
 .TP
 .B adduser \fIUSERNAME PASSWORD \fR[\fIRIGHTS\fR]
 Create a new user with the given username and password.
@@ -115,7 +122,7 @@ List all the files in \fIDIRECTORY\fR in a response body.
 If \fIREGEXP\fR is present only matching files are returned.
 .TP
 .B get \fITRACK\fR \fIPREF\fR
-Getsa preference value.
+Gets a preference value.
 On success the second field of the response line will have the value.
 .IP
 If the track or preference do not exist then the response code is 555.
@@ -164,7 +171,7 @@ depending on how the tracks came to be added to the queue.
 .TP
 .B new \fR[\fIMAX\fR]
 Send the most recently added \fIMAX\fR tracks in a response body.
-If the argument is ommitted, the \fBnew_max\fR most recent tracks are
+If the argument is omitted, the \fBnew_max\fR most recent tracks are
 listed (see \fBdisorder_config\fR(5)).
 .TP
 .B nop
@@ -200,6 +207,16 @@ Add a track to the queue.
 The response contains the queue ID of the track.
 Requires the \fBplay\fR right.
 .TP
+.B playafter \fITARGET\fR \fITRACK\fR ...
+Add all the tracks in the \fITRACK\fR list to the queue after \fITARGET\fR
+(which should be a track ID).
+If \fITARGET\fR is the empty string then the listed tracks are put
+at the head of the queue.
+.IP
+Currently the success result does \fInot\fR include the new track IDs.
+.IP
+Requires the \fBplay\fR right.
+.TP
 .B playing
 Report what track is playing.
 .IP
@@ -208,6 +225,44 @@ track information (see below).
 .IP
 If the response is \fB259\fR then nothing is playing.
 .TP
+.B playlist-delete \fIPLAYLIST\fR
+Delete a playlist.
+Requires permission to modify that playlist and the \fBplay\fR right.
+.TP
+.B playlist-get \fIPLAYLIST\fR
+Get the contents of a playlist, in a response body.
+Requires permission to read that playlist and the \fBread\fR right.
+If the playlist does not exist the response is 555.
+.TP
+.B playlist-get-share \fIPLAYLIST\fR
+Get the sharing status of a playlist.
+The result will be \fBpublic\fR, \fBprivate\fR or \fBshared\fR.
+Requires permission to read that playlist and the \fBread\fR right.
+.TP
+.B playlist-lock \fIPLAYLIST\fR
+Lock a playlist.
+Requires permission to modify that playlist and the \fBplay\fR right.
+Only one playlist may be locked at a time on a given connection and the lock
+automatically expires when the connection is closed.
+.TP
+.B playlist-set \fIPLAYLIST\fR
+Set the contents of a playlist.
+The new contents should be supplied in a command body.
+Requires permission to modify that playlist and the \fBplay\fR right.
+The playlist must be locked.
+.TP
+.B playlist-set-share \fIPLAYLIST\fR \fISHARE\fR
+Set the sharing status of a playlist to
+\fBpublic\fR, \fBprivate\fR or \fBshared\fR.
+Requires permission to modify that playlist and the \fBplay\fR right.
+.TP
+.B playlist-unlock\fR
+Unlock the locked playlist.
+.TP
+.B playlists
+List all playlists that this connection has permission to read.
+Requires the \fBread\fR right.
+.TP
 .B prefs \fBTRACK\fR
 Send back the preferences for \fITRACK\fR in a response body.
 Each line of the response has the usual line syntax, the first field being the
@@ -238,6 +293,11 @@ See below for the track information syntax.
 .B reconfigure
 Request that DisOrder reconfigure itself.
 Requires the \fBadmin\fR right.
+.IP
+Not all configuration options can be modified during the lifetime of the
+server; of those that can't, some will just be ignored if they change while
+others will cause the new configuration to be rejected.
+See \fBdisorder_config\fR(5) for details.
 .TP
 .B register \fIUSERNAME PASSWORD EMAIL
 Register a new user.
@@ -416,7 +476,7 @@ With two parameters sets each side independently.
 Setting the volume requires the \fBvolume\fR right.
 .SH RESPONSES
 Responses are three-digit codes.
-The first digit distinguishes errors from succesful responses:
+The first digit distinguishes errors from successful responses:
 .TP
 .B 2
 Operation succeeded.
@@ -593,6 +653,21 @@ Further details aren't included any more.
 .B playing \fITRACK\fR [\fIUSERNAME\fR]
 Started playing \fITRACK\fR.
 .TP
+.B playlist_created \fIPLAYLIST\fR \fISHARING\fR
+Sent when a playlist is created.
+For private playlists this is intended to be sent only to the owner (but
+this is not currently implemented).
+.TP
+.B playlist_deleted \fIPLAYLIST\fR
+Sent when a playlist is deleted.
+For private playlists this is intended to be sent only to the owner (but
+this is not currently implemented).
+.TP
+.B playlist_modified \fIPLAYLIST\fR \fISHARING\fR
+Sent when a playlist is modified (either its contents or its sharing status).
+For private playlists this is intended to be sent only to the owner (but
+this is not currently implemented).
+.TP
 .B queue \fIQUEUE-ENTRY\fR...
 Added \fITRACK\fR to the queue.
 .TP
index a575c58..f194a0e 100644 (file)
@@ -112,6 +112,11 @@ automatically, and thus has relatively modest storage requirements.
 .TP 8
 .B SIGHUP
 Re-read the configuration file.
+.IP
+Not all configuration options can be modified during the lifetime of the
+server; of those that can't, some will just be ignored if they change while
+others will cause the new configuration to be rejected.
+See \fBdisorder_config\fR(5) for details.
 .TP
 .B SIGTERM
 Terminate the daemon gracefully.
diff --git a/driver/Makefile.am b/driver/Makefile.am
deleted file mode 100644 (file)
index df8fe1f..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# This file is part of DisOrder
-# Copyright (C) 2005, 2007, 2008 Richard Kettlewell
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-
-
-aolib_LTLIBRARIES=libdisorder.la
-aolibdir=${libdir}/ao/plugins-2
-finkaolibdir=${finkdir}/lib/ao/plugins-2
-usraolibdir=/usr/lib/ao/plugins-2
-AM_CPPFLAGS=-I${top_srcdir}/lib
-
-libdisorder_la_SOURCES=disorder.c
-libdisorder_la_LDFLAGS=-module
-
-install-data-hook:
-
-# Link ao driver into right location.  If you have some other location then
-# you'll need to modify this or link it manually.
-#
-# We don't mess with this for now; since disorder-decode covers some common
-# cases, the libao driver is less useful than it was.
-link-ao-driver:
-       @if test -d ${DESTDIR}${finkaolibdir} \
-          && test ${finkaolibdir} != ${aolibdir}; then \
-         echo rm -f ${DESTDIR}${finkaolibdir}/libdisorder.*; \
-         rm -f ${DESTDIR}${finkaolibdir}/libdisorder.*; \
-         echo ln ${aolibdir}/libdisorder.* ${DESTDIR}${finkaolibdir}; \
-         ln ${DESTDIR}${aolibdir}/libdisorder.* ${DESTDIR}${finkaolibdir}; \
-       fi
-       @if test -d ${DESTDIR}${usraolibdir} \
-          && test ${usraolibdir} != ${aolibdir}; then \
-         echo rm -f ${DESTDIR}${usraolibdir}/libdisorder.*; \
-         rm -f ${DESTDIR}${usraolibdir}/libdisorder.*; \
-         echo ln ${DESTDIR}${aolibdir}/libdisorder.* ${DESTDIR}${usraolibdir}; \
-         ln ${DESTDIR}${aolibdir}/libdisorder.* ${DESTDIR}${usraolibdir}; \
-       fi
diff --git a/driver/disorder.c b/driver/disorder.c
deleted file mode 100644 (file)
index e6b6575..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * This file is part of DisOrder.
- * Copyright (C) 2005, 2007 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file driver/disorder.c
- * @brief libao driver used by DisOrder
- *
- * The output from this driver is expected to be fed to @c
- * disorder-normalize to convert to the confnigured target format.
- */
-
-#include "common.h"
-
-#include <errno.h>
-#include <unistd.h>
-#include <poll.h>
-#include <ao/ao.h>
-#include <ao/plugin.h>
-
-#include "speaker-protocol.h"
-
-/* extra declarations to help out lazy <ao/plugin.h> */
-int ao_plugin_test(void);
-ao_info *ao_plugin_driver_info(void);
-char *ao_plugin_file_extension(void);
-
-/** @brief Private data structure for this driver */
-struct internal {
-  int fd;                              /* output file descriptor */
-  int exit_on_error;                   /* exit on write error */
-
-  /** @brief Record of sample format */
-  struct stream_header header;
-
-};
-
-/* like write() but never returns EINTR/EAGAIN or short */
-static int do_write(int fd, const void *ptr, size_t n) {
-  size_t written = 0;
-  int ret;
-  struct pollfd ufd;
-
-  memset(&ufd, 0, sizeof ufd);
-  ufd.fd = fd;
-  ufd.events = POLLOUT;
-  while(written < n) {
-    ret = write(fd, (const char *)ptr + written, n - written);
-    if(ret < 0) {
-      switch(errno) {
-      case EINTR: break;
-      case EAGAIN:
-       /* Someone sneakily gave us a nonblocking file descriptor, wait until
-        * we can write again */
-       ret = poll(&ufd, 1, -1);
-       if(ret < 0 && errno != EINTR) return -1;
-       break;
-      default:
-       return -1;
-      }
-    } else
-      written += ret;
-  }
-  return written;
-}
-
-/* return 1 if this driver can be opened */
-int ao_plugin_test(void) {
-  return 1;
-}
-
-/* return info about this driver */
-ao_info *ao_plugin_driver_info(void) {
-  static const char *options[] = { "fd" };
-  static const ao_info info = {
-    AO_TYPE_LIVE,                      /* type */
-    (char *)"DisOrder format driver",  /* name */
-    (char *)"disorder",                        /* short_name */
-    (char *)"http://www.greenend.org.uk/rjk/disorder/", /* comment */
-    (char *)"Richard Kettlewell",      /* author */
-    AO_FMT_NATIVE,                     /* preferred_byte_format */
-    0,                                 /* priority */
-    (char **)options,                  /* options */
-    1,                                 /* option_count */
-  };
-  return (ao_info *)&info;
-}
-
-/* initialize the private data structure */
-int ao_plugin_device_init(ao_device *device) {
-  struct internal *i = malloc(sizeof (struct internal));
-  const char *e;
-
-  if(!i) return 0;
-  memset(i, 0, sizeof *i);
-  if((e = getenv("DISORDER_RAW_FD")))
-    i->fd = atoi(e);
-  else
-    i->fd = 1;
-  device->internal = i;
-  return 1;
-}
-
-/* set an option */
-int ao_plugin_set_option(ao_device *device,
-                        const char *key,
-                        const char *value) {
-  struct internal *i = device->internal;
-
-  if(!strcmp(key, "fd"))
-    i->fd = atoi(value);
-  else if(!strcmp(key, "fragile"))
-    i->exit_on_error = atoi(value);
-  /* unknown options are required to be ignored */
-  return 1;
-}
-
-/* open the device */
-int ao_plugin_open(ao_device *device, ao_sample_format *format) {
-  struct internal *i = device->internal;
-  
-  /* we would like native-order samples */
-  device->driver_byte_format = AO_FMT_NATIVE;
-  i->header.rate = format->rate;
-  i->header.channels = format->channels;
-  i->header.bits = format->bits;
-  i->header.endian = ENDIAN_NATIVE;
-  return 1;
-}
-
-/* play some samples */
-int ao_plugin_play(ao_device *device, const char *output_samples, 
-                  uint_32 num_bytes) {
-  struct internal *i = device->internal;
-
-  /* Fill in and write the header */
-  i->header.nbytes = num_bytes;
-  if(do_write(i->fd, &i->header, sizeof i->header) < 0) {
-    if(i->exit_on_error) _exit(-1);
-    return 0;
-  }
-
-  /* Write the sample data */
-  if(do_write(i->fd, output_samples, num_bytes) < 0) {
-    if(i->exit_on_error) _exit(-1);
-    return 0;
-  }
-  return 1;
-}
-
-/* close the device */
-int ao_plugin_close(ao_device attribute((unused)) *device) {
-  return 1;
-}
-
-/* delete private data structures */
-void ao_plugin_device_clear(ao_device *device) {
-  free(device->internal);
-  device->internal = 0;
-}
-
-/* report preferred filename extension */
-char *ao_plugin_file_extension(void) {
-  return 0;
-}
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-End:
-*/
index 109a7e2..36a6ad5 100644 (file)
@@ -1,6 +1,6 @@
 #
 # This file is part of DisOrder.
-# Copyright (C) 2005-2008 Richard Kettlewell
+# Copyright (C) 2005-2010 Richard Kettlewell
 #
 # 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
@@ -27,6 +27,29 @@ disobedience32x32.xpm cross.svg go.svg notes.svg noteson.svg pause.svg       \
 query.svg queryon.svg speaker.svg speakeron.svg cross32.png            \
 pause32.png play32.png playdisabled32.png playenabled32.png            \
 randomdisabled32.png randomenabled32.png rtpdisabled32.png             \
-rtpenabled32.png
+rtpenabled32.png duck55.png cards24.png cards48.png                    \
+cards-simple-fanned.svg cards-thin.svg
 
-CLEANFILES=$(SEDFILES)
+DISOBEDIENCE_IMAGES=up.png down.png cards24.png logo256.png duck.png   \
+propagate.png
+
+if GTK
+noinst_HEADERS=images.h
+
+images.h: $(DISOBEDIENCE_IMAGES)
+       set -e;                                                         \
+       exec > @$.new;                                                  \
+       for png in $^; do                                               \
+         name=`echo $$png | $(GNUSED) 's,.*/,,;s,\.png,,;'`;           \
+         gdk-pixbuf-csource --raw --name=image_$$name $$png;           \
+       done;                                                           \
+       echo "static const struct image images[] = {";                  \
+       for png in `echo $^`; do                                        \
+         name=`echo $$png | $(GNUSED) 's,.*/,,;s,\.png,,;'`;           \
+         echo "  { \"$$name.png\", image_$$name },";                   \
+       done|sort;                                                      \
+       echo "};"
+       mv @$.new $@
+endif
+
+CLEANFILES=$(SEDFILES) images.h
diff --git a/images/cards-simple-fanned.svg b/images/cards-simple-fanned.svg
new file mode 100644 (file)
index 0000000..9ae9730
--- /dev/null
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="48"
+   height="48"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="cards-simple-fanned.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/richard/cards/cards-simple-fanned-24.png"
+   inkscape:export-xdpi="45"
+   inkscape:export-ydpi="45">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="17.25"
+     inkscape:cx="24"
+     inkscape:cy="24"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:snap-intersection-line-segments="true"
+     inkscape:snap-bbox="true"
+     inkscape:snap-nodes="false"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:window-width="1301"
+     inkscape:window-height="1034"
+     inkscape:window-x="122"
+     inkscape:window-y="20">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2383"
+       visible="true"
+       enabled="true"
+       empspacing="8" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="g3227">
+      <rect
+         transform="matrix(0.9659258,-0.2588191,0.2588191,0.9659258,0,0)"
+         y="8.3028555"
+         x="1.4500779"
+         height="34.000004"
+         width="24.000002"
+         id="rect3292"
+         style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.00000024;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         transform="matrix(0.9659258,-0.2588191,0.2588191,0.9659258,0,0)"
+         y="9.3028555"
+         x="2.450078"
+         height="32.000004"
+         width="22.000002"
+         id="rect3294"
+         style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2.00000023999999987;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         transform="matrix(0.9659258,-0.2588191,0.2588191,0.9659258,0,0)"
+         y="13.302856"
+         x="6.4500785"
+         height="24.000002"
+         width="14.000001"
+         id="rect3296"
+         style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:2.00000023999999987;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+    <g
+       id="g3222">
+      <rect
+         y="6.5112176"
+         x="12.457951"
+         height="34"
+         width="24"
+         id="rect3267"
+         style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         y="7.5112176"
+         x="13.457951"
+         height="32"
+         width="22"
+         id="rect2387"
+         style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         y="11.511218"
+         x="17.457951"
+         height="24"
+         width="14"
+         id="rect3159"
+         style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:2;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+    <g
+       id="g3217">
+      <rect
+         transform="matrix(0.9659258,0.2588191,-0.2588191,0.9659258,0,0)"
+         y="1.7532461"
+         x="22.488214"
+         height="34.000004"
+         width="24.000002"
+         id="rect3276"
+         style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.00000024;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         transform="matrix(0.9659258,0.2588191,-0.2588191,0.9659258,0,0)"
+         y="2.7532461"
+         x="23.488214"
+         height="32.000004"
+         width="22.000002"
+         id="rect3278"
+         style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2.00000023999999987;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         transform="matrix(0.9659258,0.2588191,-0.2588191,0.9659258,0,0)"
+         y="6.7532463"
+         x="27.488214"
+         height="24.000002"
+         width="14.000001"
+         id="rect3280"
+         style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:2.00000023999999987;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>
diff --git a/images/cards-thin.svg b/images/cards-thin.svg
new file mode 100644 (file)
index 0000000..94e1255
--- /dev/null
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="48"
+   height="48"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="cards-thin.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/richard/cards48.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="17.1875"
+     inkscape:cx="24"
+     inkscape:cy="24"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:snap-intersection-line-segments="true"
+     inkscape:snap-bbox="true"
+     inkscape:snap-nodes="false"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:window-width="1639"
+     inkscape:window-height="1031"
+     inkscape:window-x="122"
+     inkscape:window-y="20">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2383"
+       visible="true"
+       enabled="true"
+       empspacing="8" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="g3290"
+       transform="matrix(0.9848078,-0.1736482,0.1736482,0.9848078,3.6830881,3.8942155)">
+      <rect
+         y="2"
+         x="2"
+         height="34"
+         width="24"
+         id="rect3292"
+         style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         y="3"
+         x="3"
+         height="32"
+         width="22"
+         id="rect3294"
+         style="fill:#ff0000;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         y="7"
+         x="7"
+         height="24"
+         width="14"
+         id="rect3296"
+         style="fill:#ff0000;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+    <g
+       id="g3269"
+       transform="translate(10,3)">
+      <rect
+         y="2"
+         x="2"
+         height="34"
+         width="24"
+         id="rect3267"
+         style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         y="3"
+         x="3"
+         height="32"
+         width="22"
+         id="rect2387"
+         style="fill:#ff0000;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         y="7"
+         x="7"
+         height="24"
+         width="14"
+         id="rect3159"
+         style="fill:#ff0000;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+    <g
+       id="g3274"
+       transform="matrix(0.9848078,0.1736482,-0.1736482,0.9848078,16.28172,3.0320659)">
+      <rect
+         y="2"
+         x="2"
+         height="34"
+         width="24"
+         id="rect3276"
+         style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         y="3"
+         x="3"
+         height="32"
+         width="22"
+         id="rect3278"
+         style="fill:#ff0000;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         y="7"
+         x="7"
+         height="24"
+         width="14"
+         id="rect3280"
+         style="fill:#ff0000;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>
diff --git a/images/cards24.png b/images/cards24.png
new file mode 100644 (file)
index 0000000..24e90fb
Binary files /dev/null and b/images/cards24.png differ
diff --git a/images/cards48.png b/images/cards48.png
new file mode 100644 (file)
index 0000000..e929b75
Binary files /dev/null and b/images/cards48.png differ
diff --git a/images/duck55.png b/images/duck55.png
new file mode 100644 (file)
index 0000000..9fc0425
Binary files /dev/null and b/images/duck55.png differ
index f7d99ac..be12f1c 100644 (file)
@@ -1,6 +1,6 @@
 #
 # This file is part of DisOrder.
-# Copyright (C) 2004-2008 Richard Kettlewell
+# Copyright (C) 2004-2009 Richard Kettlewell
 #
 # 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
@@ -20,25 +20,26 @@ noinst_LIBRARIES=libdisorder.a
 include_HEADERS=disorder.h
 
 if SERVER
-TRACKDB=trackdb.c
+TRACKDB=trackdb.c trackdb-playlists.c
 else
 TRACKDB=trackdb-stub.c
 endif
 
 libdisorder_a_SOURCES=charset.c charset.h              \
        addr.c addr.h                                   \
-       alsabg.c alsabg.h                               \
        arcfour.c arcfour.h                             \
        authhash.c authhash.h                           \
        basen.c basen.h                                 \
        base64.c base64.h                               \
        bits.c bits.h                                   \
+       byte-order.h                                    \
        cache.c cache.h                                 \
        cgi.c cgi.h                                     \
        client.c client.h                               \
        client-common.c client-common.h                 \
        configuration.c configuration.h                 \
        cookies.c cookies.h                             \
+       coreaudio.c coreaudio.h                         \
        dateparse.c dateparse.h xgetdate.c              \
        defs.c defs.h                                   \
        eclient.c eclient.h                             \
@@ -54,17 +55,17 @@ libdisorder_a_SOURCES=charset.c charset.h           \
        ifreq.c ifreq.h                                 \
        inputline.c inputline.h                         \
        kvp.c kvp.h                                     \
-       log.c log.h log-impl.h                          \
+       log.c log.h                                     \
        logfd.c logfd.h                                 \
        macros.c macros-builtin.c macros.h              \
-       mem.c mem.h mem-impl.h                          \
+       mem.c mem.h                                     \
        mime.h mime.c                                   \
-       mixer.c mixer.h mixer-oss.c mixer-alsa.c        \
        printf.c printf.h                               \
        asprintf.c fprintf.c snprintf.c                 \
        queue.c queue.h                                 \
        random.c random.h                               \
        regsub.c regsub.h                               \
+       resample.c resample.h                           \
        rights.c queue-rights.c rights.h                \
        rtp.h                                           \
        selection.c selection.h                         \
@@ -81,11 +82,16 @@ libdisorder_a_SOURCES=charset.c charset.h           \
        $(TRACKDB) trackdb.h trackdb-int.h              \
        trackname.c trackorder.c trackname.h            \
        tracksort.c                                     \
+       uaudio.c uaudio-thread.c uaudio.h uaudio-apis.c \
+       uaudio-oss.c uaudio-alsa.c                      \
+       uaudio-coreaudio.c                              \
+       uaudio-rtp.c uaudio-command.c uaudio-schedule.c \
        url.h url.c                                     \
        user.h user.c                                   \
        unicode.h unicode.c                             \
        unidata.h unidata.c                             \
        vacopy.h                                        \
+       validity.c validity.h                           \
        vector.c vector.h                               \
        version.c version.h                             \
        wav.h wav.c                                     \
@@ -116,6 +122,7 @@ definitions.h: Makefile
        echo "#define PKGCONFDIR \"${sysconfdir}/\"PACKAGE" >> $@.new
        echo "#define PKGSTATEDIR \"${localstatedir}/\"PACKAGE" >> $@.new
        echo "#define PKGDATADIR \"${pkgdatadir}/\"" >> $@.new
+       echo "#define DOCHTMLDIR \"${dochtmldir}\"" >> $@.new
        echo "#define SBINDIR \"${sbindir}/\"" >> $@.new
        echo "#define BINDIR \"${bindir}/\"" >> $@.new
        echo "#define FINKBINDIR \"${finkbindir}/\"" >> $@.new
@@ -129,10 +136,6 @@ defs.lo: definitions.h versionstring.h
 rebuild-unicode:
        cd ${srcdir} && ${top_srcdir}/scripts/make-unidata
 
-%.i: %.c
-       $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) -c $< > $@.new
-       mv $@.new $@
-
 CLEANFILES=definitions.h definitions.h.new version-string versionstring.h \
           *.gcda *.gcov *.gcno *.c.html index.html
 
index fecc28c..052466c 100644 (file)
@@ -30,6 +30,9 @@
 #include "printf.h"
 #include "addr.h"
 #include "mem.h"
+#include "syscalls.h"
+#include "configuration.h"
+#include "vector.h"
 
 /** @brief Convert a pair of strings to an address
  * @param a Pointer to string list
@@ -60,23 +63,24 @@ struct addrinfo *get_address(const struct stringlist *a,
   case 1:
     byte_xasprintf(&name, "host * service %s", a->s[0]);
     if((rc = getaddrinfo(0, a->s[0], pref, &res))) {
-      error(0, "getaddrinfo %s: %s", a->s[0], gai_strerror(rc));
+      disorder_error(0, "getaddrinfo %s: %s", a->s[0], gai_strerror(rc));
       return 0;
     }
     break;
   case 2:
     byte_xasprintf(&name, "host %s service %s", a->s[0], a->s[1]);
     if((rc = getaddrinfo(a->s[0], a->s[1], pref, &res))) {
-      error(0, "getaddrinfo %s %s: %s", a->s[0], a->s[1], gai_strerror(rc));
+      disorder_error(0, "getaddrinfo %s %s: %s",
+                    a->s[0], a->s[1], gai_strerror(rc));
       return 0;
     }
     break;
   default:
-    error(0, "invalid network address specification (n=%d)", a->n);
+    disorder_error(0, "invalid network address specification (n=%d)", a->n);
     return 0;
   }
   if(!res || (pref && res->ai_socktype != pref->ai_socktype)) {
-    error(0, "getaddrinfo didn't give us a suitable socket address");
+    disorder_error(0, "getaddrinfo didn't give us a suitable socket address");
     if(res)
       freeaddrinfo(res);
     return 0;
@@ -92,29 +96,38 @@ struct addrinfo *get_address(const struct stringlist *a,
  */
 int addrinfocmp(const struct addrinfo *a,
                const struct addrinfo *b) {
-  const struct sockaddr_in *ina, *inb;
-  const struct sockaddr_in6 *in6a, *in6b;
-  
   if(a->ai_family != b->ai_family) return a->ai_family - b->ai_family;
   if(a->ai_socktype != b->ai_socktype) return a->ai_socktype - b->ai_socktype;
   if(a->ai_protocol != b->ai_protocol) return a->ai_protocol - b->ai_protocol;
-  switch(a->ai_family) {
+  return sockaddrcmp(a->ai_addr, b->ai_addr);
+}
+
+/** @brief Comparison function for socket addresses
+ *
+ * Suitable for qsort().
+ */
+int sockaddrcmp(const struct sockaddr *a,
+               const struct sockaddr *b) {
+  const struct sockaddr_in *ina, *inb;
+  const struct sockaddr_in6 *in6a, *in6b;
+  
+  if(a->sa_family != b->sa_family) return a->sa_family - b->sa_family;
+  switch(a->sa_family) {
   case PF_INET:
-    ina = (const struct sockaddr_in *)a->ai_addr;
-    inb = (const struct sockaddr_in *)b->ai_addr;
+    ina = (const struct sockaddr_in *)a;
+    inb = (const struct sockaddr_in *)b;
     if(ina->sin_port != inb->sin_port) return ina->sin_port - inb->sin_port;
     return ina->sin_addr.s_addr - inb->sin_addr.s_addr;
     break;
   case PF_INET6:
-    in6a = (const struct sockaddr_in6 *)a->ai_addr;
-    in6b = (const struct sockaddr_in6 *)b->ai_addr;
+    in6a = (const struct sockaddr_in6 *)a;
+    in6b = (const struct sockaddr_in6 *)b;
     if(in6a->sin6_port != in6b->sin6_port)
       return in6a->sin6_port - in6b->sin6_port;
     return memcmp(&in6a->sin6_addr, &in6b->sin6_addr,
                  sizeof (struct in6_addr));
   default:
-    error(0, "unsupported protocol family %d", a->ai_protocol);
-    return memcmp(a->ai_addr, b->ai_addr, a->ai_addrlen); /* kludge */
+    disorder_fatal(0, "unsupported protocol family %d", a->sa_family);
   }
 }
 
@@ -194,6 +207,142 @@ char *format_sockaddr(const struct sockaddr *sa) {
   }
 }
 
+/** @brief Parse the text form of a network address
+ * @param na Where to store result
+ * @param nvec Number of strings
+ * @param vec List of strings
+ * @return 0 on success, -1 on syntax error
+ */
+int netaddress_parse(struct netaddress *na,
+                    int nvec,
+                    char **vec) {
+  const char *port;
+
+  na->af = AF_UNSPEC;
+  if(nvec > 0 && vec[0][0] == '-') {
+    if(!strcmp(vec[0], "-4"))
+      na->af = AF_INET;
+    else if(!strcmp(vec[0], "-6")) 
+      na->af = AF_INET6;
+    else if(!strcmp(vec[0], "-unix"))
+      na->af = AF_UNIX;
+    else if(!strcmp(vec[0], "-"))
+      na->af = AF_UNSPEC;
+    else
+      return -1;
+    --nvec;
+    ++vec;
+  }
+  if(nvec == 0)
+    return -1;
+  /* Possibilities are:
+   *
+   *       /path/to/unix/socket      an AF_UNIX socket
+   *       * PORT                    any address, specific port
+   *       PORT                      any address, specific port
+   *       ADDRESS PORT              specific address, specific port
+   */
+  if(vec[0][0] == '/' && na->af == AF_UNSPEC)
+    na->af = AF_UNIX;
+  if(na->af == AF_UNIX) {
+    if(nvec != 1)
+      return -1;
+    na->address = xstrdup(vec[0]);
+    na->port = -1;                     /* makes no sense */
+  } else {
+    switch(nvec) {
+    case 1:
+      na->address = NULL;
+      port = vec[0];
+      break;
+    case 2:
+      if(!strcmp(vec[0], "*"))
+       na->address = NULL;
+      else
+       na->address = xstrdup(vec[0]);
+      port = vec[1];
+      break;
+    default:
+      return -1;
+    }
+    if(port[strspn(port, "0123456789")])
+      return -1;
+    long p;
+    int e = xstrtol(&p, port, NULL, 10);
+
+    if(e)
+      return -1;
+    if(p > 65535)
+      return -1;
+    na->port = (int)p;
+  }
+  return 0;
+}
+
+/** @brief Format a @ref netaddress structure
+ * @param na Network address to format
+ * @param nvecp Where to put string count, or NULL
+ * @param vecp Where to put strings
+ *
+ * The formatted form is suitable for passing to netaddress_parse().
+ */
+void netaddress_format(const struct netaddress *na,
+                      int *nvecp,
+                      char ***vecp) {
+  struct vector v[1];
+
+  vector_init(v);
+  switch(na->af) {
+  case AF_UNSPEC: vector_append(v, xstrdup("-")); break;
+  case AF_INET: vector_append(v, xstrdup("-4")); break;
+  case AF_INET6: vector_append(v, xstrdup("-6")); break;
+  case AF_UNIX: vector_append(v, xstrdup("-unix")); break;
+  }
+  if(na->address)
+    vector_append(v, xstrdup(na->address));
+  else
+    vector_append(v, xstrdup("*"));
+  if(na->port != -1) {
+    char buffer[64];
+
+    snprintf(buffer, sizeof buffer, "%d", na->port);
+    vector_append(v, xstrdup(buffer));
+  }
+  vector_terminate(v);
+  if(nvecp)
+    *nvecp = v->nvec;
+  if(vecp)
+    *vecp = v->vec;
+}
+
+/** @brief Resolve a network address
+ * @param na Address structure
+ * @param passive True if passive (bindable) address is desired
+ * @param protocol Protocol number desired (e.g. @c IPPROTO_TCP)
+ * @return List of suitable addresses or NULL
+ */
+struct addrinfo *netaddress_resolve(const struct netaddress *na,
+                                   int passive,
+                                   int protocol) {
+  struct addrinfo *res, hints[1];
+  char service[64];
+  int rc;
+
+  memset(hints, 0, sizeof hints);
+  hints->ai_family = na->af;
+  hints->ai_protocol = protocol;
+  hints->ai_flags = passive ? AI_PASSIVE : 0;
+  snprintf(service, sizeof service, "%d", na->port);
+  rc = getaddrinfo(na->address, service, hints, &res);
+  if(rc) {
+    disorder_error(0, "getaddrinfo %s %d: %s",
+                  na->address ? na->address : "*",
+                  na->port, gai_strerror(rc));
+    return NULL;
+  }
+  return res;
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index 973733f..90c165e 100644 (file)
 
 #include <netdb.h>
 
-#include "configuration.h"
+struct stringlist;
+
+/** @brief A network address */
+struct netaddress {
+  /** @brief Address family
+   *
+   * Typically @c AF_UNIX, @c AF_INET, @c AF_INET6 or @c AF_UNSPEC.
+   * Set to -1 to mean 'no address'.
+   */
+  int af;
+
+  /** @brief Address or NULL for 'any' */
+  char *address;
+
+  /** @brief Port number */
+  int port;
+};
 
 struct addrinfo *get_address(const struct stringlist *a,
                             const struct addrinfo *pref,
@@ -31,10 +47,22 @@ struct addrinfo *get_address(const struct stringlist *a,
 
 int addrinfocmp(const struct addrinfo *a,
                const struct addrinfo *b);
+int sockaddrcmp(const struct sockaddr *a,
+               const struct sockaddr *b);
 
 int multicast(const struct sockaddr *sa);
 char *format_sockaddr(const struct sockaddr *sa);
 
+int netaddress_parse(struct netaddress *na,
+                    int nvec,
+                    char **vec);
+void netaddress_format(const struct netaddress *na,
+                      int *nvecp,
+                      char ***vecp);
+struct addrinfo *netaddress_resolve(const struct netaddress *na,
+                                   int passive,
+                                   int protocol);
+
 #endif /* ADDR_H */
 
 /*
diff --git a/lib/alsabg.c b/lib/alsabg.c
deleted file mode 100644 (file)
index 56e5c6a..0000000
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * This file is part of DisOrder.
- * Copyright (C) 2008 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file alsabg.c
- * @brief Background-thread interface to ALSA
- *
- * This wraps ALSA with an interface which calls back to the client from a
- * thread.  It's not intended for completely general use, just what DisOrder
- * needs.
- *
- * Only builds on Linux systems.
- */
-
-#include "common.h"
-
-#if HAVE_ALSA_ASOUNDLIB_H
-#include <alsa/asoundlib.h>
-#include <pthread.h>
-
-#include "alsabg.h"
-#include "log.h"
-
-/** @brief Output handle */
-static snd_pcm_t *pcm;
-
-/** @brief Called to get audio data */
-static alsa_bg_supply *supplyfn;
-
-static pthread_t alsa_bg_collect_tid, alsa_bg_play_tid;
-
-/** @brief Set to shut down the background threads */
-static int alsa_bg_shutdown = 0;
-
-/** @brief Number of channels (samples per frame) */
-#define CHANNELS 2
-
-/** @brief Number of bytes per samples */
-#define BYTES_PER_SAMPLE 2
-
-/** @brief Number of bytes per frame */
-#define BYTES_PER_FRAME (CHANNELS * BYTES_PER_SAMPLE)
-
-/** @brief Buffer size in bytes */
-#define BUFFER_BYTES 65536
-
-/** @brief Buffer size in frames */
-#define BUFFER_FRAMES (BUFFER_BYTES / BYTES_PER_FRAME)
-
-/** @brief Buffer size in samples */
-#define BUFFER_SAMPLES (BUFFER_BYTES / BYTES_PER_SAMPLE)
-
-/** @brief Audio buffer */
-static uint8_t alsa_bg_buffer[BUFFER_BYTES];
-
-/** @brief First playable byte in audio buffer */
-static unsigned alsa_bg_start;
-
-/** @brief Number of playable bytes in audio buffer */
-static unsigned alsa_bg_count;
-
-/** @brief Current enable status */
-static int alsa_bg_enabled;
-
-/** @brief Lock protecting audio buffer pointers */
-static pthread_mutex_t alsa_bg_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/** @brief Signaled when buffer contents changes */
-static pthread_cond_t alsa_bg_cond = PTHREAD_COND_INITIALIZER;
-
-/** @brief Call a pthread_ function and fatal() on exit */
-#define ep(x) do {                             \
-  int prc;                                     \
-                                               \
-  if((prc = (x)))                              \
-    fatal(prc, "%s", #x);                      \
-} while(0)
-
-/** @brief Data collection thread
- *
- * This thread collects audio data to play and stores it in the ring
- * buffer.
- */
-static void *alsa_bg_collect(void attribute((unused)) *arg) {
-  unsigned avail_start, avail_count;
-  int count;
-
-  ep(pthread_mutex_lock(&alsa_bg_lock));
-  for(;;) {
-    /* If we're shutting down, quit straight away */
-    if(alsa_bg_shutdown)
-      break;
-    /* While we're disabled or the buffer is full, just wait */
-    if(!alsa_bg_enabled || alsa_bg_count == BUFFER_BYTES) {
-      ep(pthread_cond_wait(&alsa_bg_cond, &alsa_bg_lock));
-      continue;
-    }
-    /* Figure out where and how big the gap we can write into is */
-    avail_start = alsa_bg_start + alsa_bg_count;
-    if(avail_start < BUFFER_BYTES)
-      avail_count = BUFFER_BYTES - avail_start;
-    else {
-      avail_start %= BUFFER_BYTES;
-      avail_count = alsa_bg_start - avail_start;
-    }
-    assert(avail_start < BUFFER_BYTES);
-    assert(avail_count <= BUFFER_BYTES);
-    assert(avail_count + alsa_bg_count <= BUFFER_BYTES);
-    ep(pthread_mutex_unlock(&alsa_bg_lock));
-    count = supplyfn(alsa_bg_buffer + avail_start,
-                    avail_count / BYTES_PER_SAMPLE);
-    ep(pthread_mutex_lock(&alsa_bg_lock));
-    alsa_bg_count += count * BYTES_PER_SAMPLE;
-    assert(alsa_bg_start < BUFFER_BYTES);
-    assert(alsa_bg_count <= BUFFER_BYTES);
-    ep(pthread_cond_signal(&alsa_bg_cond));
-  }
-  ep(pthread_mutex_unlock(&alsa_bg_lock));
-  return 0;
-}
-
-/** @brief Playback thread
- *
- * This thread reads audio data out of the ring buffer and plays it back
- */
-static void *alsa_bg_play(void attribute((unused)) *arg) {
-  int prepared = 1, err;
-  int start, nbytes, nframes, rframes;
-  
-  ep(pthread_mutex_lock(&alsa_bg_lock));
-  for(;;) {
-    /* If we're shutting down, quit straight away */
-    if(alsa_bg_shutdown)
-      break;
-    /* Wait for some data to be available.  (If we are marked disabled
-     * we keep on playing what we've got.) */
-    if(alsa_bg_count == 0) {
-      if(prepared) {
-       if((err = snd_pcm_drain(pcm)))
-         fatal(0, "snd_pcm_drain: %d", err);
-       prepared = 0;
-      }
-      ep(pthread_cond_wait(&alsa_bg_cond, &alsa_bg_lock));
-      continue;
-    }
-    /* Calculate how much we can play */
-    start = alsa_bg_start;
-    if(start + alsa_bg_count <= BUFFER_BYTES)
-      nbytes = alsa_bg_count;
-    else
-      nbytes = BUFFER_BYTES - start;
-    /* Limit how much of the buffer we play.  The effect is that we return from
-     * _writei earlier, and therefore free up more buffer space to read fresh
-     * data into.  /2 works fine, /4 is just conservative.  /1 (i.e. abolishing
-     * the heuristic) produces noticably noisy output. */
-    if(nbytes > BUFFER_BYTES / 4)
-      nbytes = BUFFER_BYTES / 4;
-    assert((unsigned)nbytes <= alsa_bg_count);
-    nframes = nbytes / BYTES_PER_FRAME;
-    ep(pthread_mutex_unlock(&alsa_bg_lock));
-    /* Make sure the PCM is prepared */
-    if(!prepared) {
-      if((err = snd_pcm_prepare(pcm)))
-       fatal(0, "snd_pcm_prepare: %d", err);
-      prepared = 1;
-    }
-    /* Play what we can */
-    rframes = snd_pcm_writei(pcm, alsa_bg_buffer + start, nframes);
-    ep(pthread_mutex_lock(&alsa_bg_lock));
-    if(rframes < 0) {
-      switch(rframes) {
-      case -EPIPE:
-        error(0, "underrun detected");
-        if((err = snd_pcm_prepare(pcm)))
-          fatal(0, "snd_pcm_prepare: %d", err);
-        break;
-      default:
-        fatal(0, "snd_pcm_writei: %d", rframes);
-      }
-    } else {
-      const int rbytes = rframes * BYTES_PER_FRAME;
-      /*fprintf(stderr, "%5d -> %5d\n", nbytes, rbytes);*/
-      /* Update the buffer pointers */
-      alsa_bg_count -= rbytes;
-      alsa_bg_start += rbytes;
-      if(alsa_bg_start >= BUFFER_BYTES)
-       alsa_bg_start -= BUFFER_BYTES;
-      assert(alsa_bg_start < BUFFER_BYTES);
-      assert(alsa_bg_count <= BUFFER_BYTES);
-      /* Let the collector know we've opened up some space */
-      ep(pthread_cond_signal(&alsa_bg_cond));
-    }
-  }
-  ep(pthread_mutex_unlock(&alsa_bg_lock));
-  return 0;
-}
-
-/** @brief Enable ALSA play */
-void alsa_bg_enable(void) {
-  ep(pthread_mutex_lock(&alsa_bg_lock));
-  alsa_bg_enabled = 1;
-  ep(pthread_cond_broadcast(&alsa_bg_cond));
-  ep(pthread_mutex_unlock(&alsa_bg_lock));
-}
-
-/** @brief Disable ALSA play */
-void alsa_bg_disable(void) {
-  ep(pthread_mutex_lock(&alsa_bg_lock));
-  alsa_bg_enabled = 0;
-  ep(pthread_cond_broadcast(&alsa_bg_cond));
-  ep(pthread_mutex_unlock(&alsa_bg_lock));
-}
-
-/** @brief Initialize background ALSA playback
- * @param device Target device or NULL to use default
- * @param supply Function to call to get audio data to play
- *
- * Playback is not initially enabled; see alsa_bg_enable().  When playback is
- * enabled, @p supply will be called in a background thread to request audio
- * data.  It should return in a timely manner, but playback happens from a
- * further thread and delays in @p supply will not delay transfer of data to
- * the sound device (provided it doesn't actually run out).
- */
-void alsa_bg_init(const char *device,
-                 alsa_bg_supply *supply) {
-  snd_pcm_hw_params_t *hwparams;
-  /* Only support one format for now */
-  const int sample_format = SND_PCM_FORMAT_S16_BE;
-  unsigned rate = 44100;
-  int err;
-
-  if((err = snd_pcm_open(&pcm,
-                         device ? device : "default",
-                         SND_PCM_STREAM_PLAYBACK,
-                         0)))
-    fatal(0, "error from snd_pcm_open: %d", err);
-  /* Set up 'hardware' parameters */
-  snd_pcm_hw_params_alloca(&hwparams);
-  if((err = snd_pcm_hw_params_any(pcm, hwparams)) < 0)
-    fatal(0, "error from snd_pcm_hw_params_any: %d", err);
-  if((err = snd_pcm_hw_params_set_access(pcm, hwparams,
-                                         SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
-    fatal(0, "error from snd_pcm_hw_params_set_access: %d", err);
-  if((err = snd_pcm_hw_params_set_format(pcm, hwparams,
-                                         sample_format)) < 0)
-    
-    fatal(0, "error from snd_pcm_hw_params_set_format (%d): %d",
-          sample_format, err);
-  if((err = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, 0)) < 0)
-    fatal(0, "error from snd_pcm_hw_params_set_rate (%d): %d",
-          rate, err);
-  if((err = snd_pcm_hw_params_set_channels(pcm, hwparams,
-                                           CHANNELS)) < 0)
-    fatal(0, "error from snd_pcm_hw_params_set_channels (%d): %d",
-          CHANNELS, err);
-  if((err = snd_pcm_hw_params(pcm, hwparams)) < 0)
-    fatal(0, "error calling snd_pcm_hw_params: %d", err);
-
-  /* Record the audio supply function */
-  supplyfn = supply;
-
-  /* Create the audio output thread */
-  alsa_bg_shutdown = 0;
-  alsa_bg_enabled = 0;
-  ep(pthread_create(&alsa_bg_collect_tid, 0, alsa_bg_collect, 0));
-  ep(pthread_create(&alsa_bg_play_tid, 0, alsa_bg_play, 0));
-}
-
-/** @brief Deinitialize background ALSA playback
- *
- * The opposite of alsa_bg_init().
- */
-void alsa_bg_close(void) {
-  void *r;
-
-  /* Notify background threads that we're shutting down */
-  ep(pthread_mutex_lock(&alsa_bg_lock));
-  alsa_bg_enabled = 0;
-  alsa_bg_shutdown = 1;
-  ep(pthread_cond_signal(&alsa_bg_cond));
-  ep(pthread_mutex_unlock(&alsa_bg_lock));
-  /* Join background threads when they're done */
-  ep(pthread_join(alsa_bg_collect_tid, &r));
-  ep(pthread_join(alsa_bg_play_tid, &r));
-  /* Close audio device */
-  snd_pcm_close(pcm);
-  pcm = 0;
-}
-
-#endif
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-indent-tabs-mode:nil
-End:
-*/
diff --git a/lib/alsabg.h b/lib/alsabg.h
deleted file mode 100644 (file)
index 78c83dc..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * This file is part of DisOrder.
- * Copyright (C) 2008 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file alsabg.h
- * @brief Background-thread interface to ALSA
- *
- * This wraps ALSA with an interface which calls back to the client from a
- * thread.  It's not intended for completely general use, just what DisOrder
- * needs.
- */
-
-#ifndef ALSABG_H
-#define ALSABG_H
-
-/** @brief Supply audio callback
- * @param dst Where to write audio data
- * @param nsamples Number of samples to write
- * @return Number of samples written
- *
- * This function should write up to @p *nsamples samples of data at
- * @p dst, and return the number of samples written, or -1 if some error
- * occurred.  It will be called in a background thread.
- */
-typedef int alsa_bg_supply(void *dst,
-                          unsigned nsamples_max);
-
-void alsa_bg_init(const char *dev,
-                 alsa_bg_supply *supply);
-
-void alsa_bg_enable(void);
-
-void alsa_bg_disable(void);
-
-void alsa_bg_close(void);
-
-#endif /* ALSABG_H */
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-indent-tabs-mode:nil
-End:
-*/
index 5400339..da37319 100644 (file)
@@ -99,7 +99,7 @@ int byte_xvasprintf(char **ptrp,
   int n;
 
   if((n = byte_vasprintf(ptrp, fmt, ap)) < 0)
-    fatal(errno, "error calling byte_vasprintf");
+    disorder_fatal(errno, "error calling byte_vasprintf");
   return n;
 }
 
index d5da9c5..8cc96f9 100644 (file)
@@ -85,7 +85,7 @@ const char *authhash(const void *challenge, size_t nchallenge,
     gcry_error_t e;
     
     if((e = gcry_md_open(&h, id, 0))) {
-      error(0, "gcry_md_open: %s", gcry_strerror(e));
+      disorder_error(0, "gcry_md_open: %s", gcry_strerror(e));
       return 0;
     }
   }
index 87d2795..7c1f9ec 100644 (file)
@@ -31,7 +31,7 @@
  * @param nwords Length of bignum
  * @return !v
  */
-static int zero(const unsigned long *v, int nwords) {
+static int zero(const uint32_t *v, int nwords) {
   int n;
 
   for(n = 0; n < nwords && !v[n]; ++n)
@@ -47,7 +47,7 @@ static int zero(const unsigned long *v, int nwords) {
  *
  * The quotient is stored in @p v.
  */
-static unsigned divide(unsigned long *v, int nwords, unsigned long m) {
+static unsigned divide(uint32_t *v, int nwords, unsigned long m) {
   unsigned long r = 0, a, b;
   int n;
 
@@ -66,6 +66,31 @@ static unsigned divide(unsigned long *v, int nwords, unsigned long m) {
   return r;
 }
 
+/** @brief Multiple v by m and add a
+ * @param v Pointer to bigendian bignum
+ * @param nwords Length of bignum
+ * @param m Value to multiply by
+ * @param a Value to add
+ * @return 0 on success, non-0 on overflow
+ *
+ * Does v = m * v + a.
+ */
+static int mla(uint32_t *v, int nwords, uint32_t m, uint32_t a) {
+  int n = nwords - 1;
+  uint32_t carry = a;
+
+  while(n >= 0) {
+    const uint64_t p = (uint64_t)v[n] * m + carry;
+    carry = (uint32_t)(p >> 32);
+    v[n] = (uint32_t)p;
+    --n;
+  }
+  /* If there is still a carry then we overflowed */
+  return !!carry;
+}
+
+static const char basen_chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
 /** @brief Convert v to a chosen base
  * @param v Pointer to bigendian bignum (modified!)
  * @param nwords Length of bignum
@@ -75,26 +100,53 @@ static unsigned divide(unsigned long *v, int nwords, unsigned long m) {
  * @return 0 on success, -1 if the buffer is too small
  *
  * Converts @p v to a string in the given base using decimal digits, lower case
- * letter sand upper case letters as digits.
+ * letters and upper case letters as digits.
+ *
+ * The inverse of nesab().
  */
-int basen(unsigned long *v,
+int basen(uint32_t *v,
          int nwords,
          char buffer[],
          size_t bufsize,
          unsigned base) {
   size_t i = bufsize;
-  static const char chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
   
   do {
     if(i <= 1)
       return -1;                       /* overflow */
-    buffer[--i] = chars[divide(v, nwords, base)];
+    buffer[--i] = basen_chars[divide(v, nwords, base)];
   } while(!zero(v, nwords));
   memmove(buffer, buffer + i, bufsize - i);
   buffer[bufsize - i] = 0;
   return 0;
 }
 
+/** @brief Convert a string back to a large integer in an arbitrary base
+ * @param v Where to store result as a bigendian bignum
+ * @param nwords Space available in @p v
+ * @param s Input string
+ * @param base Number base (2..62)
+ * @return 0 on success, non-0 on overflow or invalid input
+ *
+ * The inverse of basen().  If the number is much smaller than the buffer then
+ * the first words will be 0.
+ */
+int nesab(uint32_t *v,
+          int nwords,
+          const char *s,
+          unsigned base) {
+  /* Initialize to 0 */
+  memset(v, 0, nwords * sizeof *v);
+  while(*s) {
+    const char *dp = strchr(basen_chars, *s++);
+    if(!dp)
+      return -1;
+    if(mla(v, nwords, base, dp - basen_chars))
+      return -1;
+  }
+  return 0;
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index 717f2ea..c1a60f0 100644 (file)
@@ -19,7 +19,7 @@
 #ifndef BASEN_H
 #define BASEN_H
 
-int basen(unsigned long *v,
+int basen(uint32_t *v,
          int nwords,
          char buffer[],
          size_t bufsize,
@@ -29,6 +29,11 @@ int basen(unsigned long *v,
  * Returns 0 on success or -1 on overflow.
  */
 
+int nesab(uint32_t *v,
+          int nwords,
+          const char *s,
+          unsigned base);
+
 #endif /* BASEN_H */
 
 /*
diff --git a/lib/byte-order.h b/lib/byte-order.h
new file mode 100644 (file)
index 0000000..e80e51f
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/byte-order.h
+ * @brief Byte order macros
+ */
+
+#ifndef BYTE_ORDER_H
+#define BYTE_ORDER_H
+
+#include <config.h>
+
+#define ENDIAN_BIG 1
+#define ENDIAN_LITTLE 2
+#ifdef WORDS_BIGENDIAN
+# define ENDIAN_NATIVE ENDIAN_BIG
+#else
+# define ENDIAN_NATIVE ENDIAN_LITTLE
+#endif
+
+#endif /* BYTE_ORDER_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index e0f69ef..52629de 100644 (file)
@@ -24,6 +24,7 @@
 #include "hash.h"
 #include "mem.h"
 #include "log.h"
+#include "syscalls.h"
 #include "cache.h"
 
 /** @brief The global cache */
@@ -60,7 +61,7 @@ void cache_put(const struct cache_type *type,
   c = xmalloc(sizeof *c);
   c->type = type;
   c->value = value;
-  time(&c->birth);
+  xtime(&c->birth);
   hash_add(h, key, c,  HASH_INSERT_OR_REPLACE);
 }
 
@@ -75,7 +76,7 @@ const void *cache_get(const struct cache_type *type, const char *key) {
   if(h
      && (c = hash_find(h, key))
      && c->type == type
-     && !expired(c, time(0)))
+     && !expired(c, xtime(0)))
     return c->value;
   else
     return 0;
@@ -98,7 +99,7 @@ void cache_expire(void) {
   time_t now;
 
   if(h) {
-    time(&now);
+    xtime(&now);
     hash_foreach(h, expiry_callback, &now);
   }
 }
index 42d599f..15b556c 100644 (file)
--- a/lib/cgi.c
+++ b/lib/cgi.c
@@ -43,7 +43,7 @@ static struct kvp *cgi__init_get(void) {
 
   if((q = getenv("QUERY_STRING")))
     return kvp_urldecode(q, strlen(q));
-  error(0, "QUERY_STRING not set, assuming empty");
+  disorder_error(0, "QUERY_STRING not set, assuming empty");
   return NULL;
 }
 
@@ -55,26 +55,26 @@ static void cgi__input(char **ptrp, size_t *np) {
   int r;
 
   if(!(cl = getenv("CONTENT_LENGTH")))
-    fatal(0, "CONTENT_LENGTH not set");
+    disorder_fatal(0, "CONTENT_LENGTH not set");
   n = atol(cl);
   /* We check for overflow and also limit the input to 16MB. Lower
    * would probably do.  */
   if(!(n+1) || n > 16 * 1024 * 1024)
-    fatal(0, "input is much too large");
+    disorder_fatal(0, "input is much too large");
   q = xmalloc_noptr(n + 1);
   while(m < n) {
     r = read(0, q + m, n - m);
     if(r > 0)
       m += r;
     else if(r == 0)
-      fatal(0, "unexpected end of file reading request body");
+      disorder_fatal(0, "unexpected end of file reading request body");
     else switch(errno) {
     case EINTR: break;
-    default: fatal(errno, "error reading request body");
+    default: disorder_fatal(errno, "error reading request body");
     }
   }
   if(memchr(q, 0, n))
-    fatal(0, "null character in request body");
+    disorder_fatal(0, "null character in request body");
   q[n + 1] = 0;
   *ptrp = q;
   if(np)
@@ -92,12 +92,12 @@ static int cgi__field_callback(const char *name, const char *value,
                                        &disposition,
                                        &pname,
                                        &pvalue))
-      fatal(0, "error parsing Content-Disposition field");
+      disorder_fatal(0, "error parsing Content-Disposition field");
     if(!strcmp(disposition, "form-data")
        && pname
        && !strcmp(pname, "name")) {
       if(*namep)
-       fatal(0, "duplicate Content-Disposition field");
+       disorder_fatal(0, "duplicate Content-Disposition field");
       *namep = pvalue;
     }
   }
@@ -109,11 +109,11 @@ static int cgi__part_callback(const char *s,
                              void *u) {
   char *name = 0;
   struct kvp *k, **head = u;
-  
+
   if(!(s = mime_parse(s, cgi__field_callback, &name)))
-    fatal(0, "error parsing part header");
+    disorder_fatal(0, "error parsing part header");
   if(!name)
-    fatal(0, "no name found");
+    disorder_fatal(0, "no name found");
   k = xmalloc(sizeof *k);
   k->next = *head;
   k->name = name;
@@ -129,7 +129,7 @@ static struct kvp *cgi__init_multipart(const char *boundary) {
   
   cgi__input(&q, 0);
   if(mime_multipart(q, cgi__part_callback, boundary, &head))
-    fatal(0, "invalid multipart object");
+    disorder_fatal(0, "invalid multipart object");
   return head;
 }
 
@@ -143,17 +143,17 @@ static struct kvp *cgi__init_post(void) {
   if(!(ct = getenv("CONTENT_TYPE")))
     ct = "application/x-www-form-urlencoded";
   if(mime_content_type(ct, &type, &k))
-    fatal(0, "invalid content type '%s'", ct);
+    disorder_fatal(0, "invalid content type '%s'", ct);
   if(!strcmp(type, "application/x-www-form-urlencoded")) {
     cgi__input(&q, &n);
     return kvp_urldecode(q, n);
   }
   if(!strcmp(type, "multipart/form-data")) {
     if(!(boundary = kvp_get(k, "boundary")))
-      fatal(0, "no boundary parameter found");
+      disorder_fatal(0, "no boundary parameter found");
     return cgi__init_multipart(boundary);
   }
-  fatal(0, "unrecognized content type '%s'", type);
+  disorder_fatal(0, "unrecognized content type '%s'", type);
 }
 
 /** @brief Initialize CGI arguments
@@ -171,18 +171,18 @@ void cgi_init(void) {
 
   cgi_args = hash_new(sizeof (char *));
   if(!(p = getenv("REQUEST_METHOD")))
-    error(0, "REQUEST_METHOD not set, assuming GET");
+    disorder_error(0, "REQUEST_METHOD not set, assuming GET");
   if(!p || !strcmp(p, "GET"))
     k = cgi__init_get();
   else if(!strcmp(p, "POST"))
     k = cgi__init_post();
   else
-    fatal(0, "unknown request method %s", p);
+    disorder_fatal(0, "unknown request method %s", p);
   /* Validate the arguments and put them in a hash */
   for(; k; k = k->next) {
     if(!utf8_valid(k->name, strlen(k->name))
        || !utf8_valid(k->value, strlen(k->value)))
-      error(0, "invalid UTF-8 sequence in cgi argument %s", k->name);
+      disorder_error(0, "invalid UTF-8 sequence in cgi argument %s", k->name);
     else
       hash_add(cgi_args, k->name, &k->value, HASH_INSERT_OR_REPLACE);
     /* We just drop bogus arguments. */
index e9c503e..fdc6d61 100644 (file)
@@ -45,7 +45,7 @@ static void *convert(const char *from, const char *to,
   size_t bufsize = 0, sl, dl;
 
   if((i = iconv_open(to, from)) == (iconv_t)-1)
-    fatal(errno, "error calling iconv_open");
+    disorder_fatal(errno, "error calling iconv_open");
   do {
     bufsize = bufsize ? 2 * bufsize : 32;
     buf = xrealloc_noptr(buf, bufsize);
@@ -59,34 +59,53 @@ static void *convert(const char *from, const char *to,
   } while(len == (size_t)-1 && errno == E2BIG);
   iconv_close(i);
   if(len == (size_t)-1) {
-    error(errno, "error converting from %s to %s", from, to);
+    disorder_error(errno, "error converting from %s to %s", from, to);
     return 0;
   }
   return buf;
 }
 
-/** @brief Convert from the local multibyte encoding to UTF-8 */
+/** @brief Convert from the local multibyte encoding to UTF-8
+ * @param mb String in current locale's multibyte encoding
+ * @return Same string in UTF-8
+ */
 char *mb2utf8(const char *mb) {
   return convert(nl_langinfo(CODESET), "UTF-8", mb, strlen(mb) + 1);
 }
 
-/** @brief Convert from UTF-8 to the local multibyte encoding */
+/** @brief Convert from UTF-8 to the local multibyte encoding
+ * @param utf8 String in UTF-8
+ * @return Same string in current locale's multibyte encoding
+ */
 char *utf82mb(const char *utf8) {
   return convert("UTF-8", nl_langinfo(CODESET), utf8, strlen(utf8) + 1);
 }
 
-/** @brief Convert from encoding @p from to UTF-8 */
+/** @brief Convert from encoding @p from to UTF-8
+ * @param from Source encoding
+ * @param any String in encoding @p from
+ * @return @p any converted to UTF-8
+ */
 char *any2utf8(const char *from, const char *any) {
   return convert(from, "UTF-8", any, strlen(any) + 1);
 }
 
-/** @brief Convert from encoding @p from to the local multibyte encoding */
+/** @brief Convert from encoding @p from to the local multibyte encoding
+ * @param from Source encoding
+ * @param any String in encoding @p from
+ * @return @p any converted to current locale's multibyte encoding
+ */
 char *any2mb(const char *from, const char *any) {
   if(from) return convert(from, nl_langinfo(CODESET), any, strlen(any) + 1);
   else return xstrdup(any);
 }
 
-/** @brief Convert from encoding @p from to encoding @p to */
+/** @brief Convert from encoding @p from to encoding @p to
+ * @param from Source encoding
+ * @param to Destination encoding
+ * @param any String in encoding @p from
+ * @return @p any converted to encoding @p to
+ */
 char *any2any(const char *from,
              const char *to,
              const char *any) {
index ced82e4..c55da89 100644 (file)
@@ -45,23 +45,17 @@ socklen_t find_server(struct config *c,
   struct addrinfo *res = 0;
   char *name;
   socklen_t len;
-   
-  static const struct addrinfo pref = {
-    .ai_flags = 0,
-    .ai_family = PF_INET,
-    .ai_socktype = SOCK_STREAM,
-    .ai_protocol = IPPROTO_TCP,
-  };
 
-  if(c->connect.n) {
-    res = get_address(&c->connect, &pref, &name);
-    if(!res) return -1;
+  if(c->connect.af != -1) {
+    res = netaddress_resolve(&c->connect, 0, IPPROTO_TCP);
+    if(!res) 
+      return -1;
     sa = res->ai_addr;
     len = res->ai_addrlen;
   } else {
     name = config_get_file2(c, "socket");
     if(strlen(name) >= sizeof su.sun_path) {
-      error(errno, "socket path is too long");
+      disorder_error(errno, "socket path is too long");
       return -1;
     }
     memset(&su, 0, sizeof su);
@@ -73,7 +67,7 @@ socklen_t find_server(struct config *c,
   *sap = xmalloc_noptr(len);
   memcpy(*sap, sa, len);
   if(namep)
-    *namep = name;
+    *namep = format_sockaddr(sa);
   if(res)
     freeaddrinfo(res);
   return len;
index 00d7cf5..67f8a53 100644 (file)
@@ -107,7 +107,7 @@ static int response(disorder_client *c, char **rp) {
     return (r[0] * 10 + r[1]) * 10 + r[2] - 111 * '0';
   } else {
     c->last = "invalid reply format";
-    error(0, "invalid reply format from %s", c->ident);
+    disorder_error(0, "invalid reply format from %s", c->ident);
     return -1;
   }
 }
@@ -144,7 +144,7 @@ static int check_response(disorder_client *c, char **rp) {
     return 0;
   } else {
     if(c->verbose)
-      error(0, "from %s: %s", c->ident, utf82mb(r));
+      disorder_error(0, "from %s: %s", c->ident, utf82mb(r));
     return rc;
   }
 }
@@ -153,6 +153,8 @@ static int check_response(disorder_client *c, char **rp) {
  * @param c Client
  * @param rp Where to store result, or NULL
  * @param cmd Command
+ * @param body Body or NULL
+ * @param nbody Length of body or -1
  * @param ap Arguments (UTF-8), terminated by (char *)0
  * @return 0 on success, non-0 on error
  *
@@ -163,16 +165,27 @@ static int check_response(disorder_client *c, char **rp) {
  *
  * NB that the response will NOT be converted to the local encoding
  * nor will quotes be stripped.  See dequote().
+ *
+ * If @p body is not NULL then the body is sent immediately after the
+ * command.  @p nbody should be the number of lines or @c -1 to count
+ * them if @p body is NULL-terminated.
+ *
+ * Usually you would call this via one of the following interfaces:
+ * - disorder_simple()
+ * - disorder_simple_body()
+ * - disorder_simple_list()
  */
 static int disorder_simple_v(disorder_client *c,
                             char **rp,
-                            const char *cmd, va_list ap) {
+                            const char *cmd,
+                             char **body, int nbody,
+                             va_list ap) {
   const char *arg;
   struct dynstr d;
 
   if(!c->fpout) {
     c->last = "not connected";
-    error(0, "not connected to server");
+    disorder_error(0, "not connected to server");
     return -1;
   }
   if(cmd) {
@@ -185,13 +198,32 @@ static int disorder_simple_v(disorder_client *c,
     dynstr_append(&d, '\n');
     dynstr_terminate(&d);
     D(("command: %s", d.vec));
-    if(fputs(d.vec, c->fpout) < 0 || fflush(c->fpout)) {
-      byte_xasprintf((char **)&c->last, "write error: %s", strerror(errno));
-      error(errno, "error writing to %s", c->ident);
-      return -1;
+    if(fputs(d.vec, c->fpout) < 0)
+      goto write_error;
+    if(body) {
+      if(nbody < 0)
+        for(nbody = 0; body[nbody]; ++nbody)
+          ;
+      for(int n = 0; n < nbody; ++n) {
+        if(body[n][0] == '.')
+          if(fputc('.', c->fpout) < 0)
+            goto write_error;
+        if(fputs(body[n], c->fpout) < 0)
+          goto write_error;
+        if(fputc('\n', c->fpout) < 0)
+          goto write_error;
+      }
+      if(fputs(".\n", c->fpout) < 0)
+        goto write_error;
     }
+    if(fflush(c->fpout))
+      goto write_error;
   }
   return check_response(c, rp);
+write_error:
+  byte_xasprintf((char **)&c->last, "write error: %s", strerror(errno));
+  disorder_error(errno, "error writing to %s", c->ident);
+  return -1;
 }
 
 /** @brief Issue a command and parse a simple response
@@ -218,7 +250,30 @@ static int disorder_simple(disorder_client *c,
   int ret;
 
   va_start(ap, cmd);
-  ret = disorder_simple_v(c, rp, cmd, ap);
+  ret = disorder_simple_v(c, rp, cmd, 0, 0, ap);
+  va_end(ap);
+  return ret;
+}
+
+/** @brief Issue a command with a body and parse a simple response
+ * @param c Client
+ * @param rp Where to store result, or NULL (UTF-8)
+ * @param body Pointer to body
+ * @param nbody Size of body
+ * @param cmd Command
+ * @return 0 on success, non-0 on error
+ *
+ * See disorder_simple().
+ */
+static int disorder_simple_body(disorder_client *c,
+                                char **rp,
+                                char **body, int nbody,
+                                const char *cmd, ...) {
+  va_list ap;
+  int ret;
+
+  va_start(ap, cmd);
+  ret = disorder_simple_v(c, rp, cmd, body, nbody, ap);
   va_end(ap);
   return ret;
 }
@@ -239,7 +294,7 @@ static int dequote(int rc, char **rp) {
       *rp = *rr;
       return 0;
     }
-    error(0, "invalid reply: %s", *rp);
+    disorder_error(0, "invalid reply: %s", *rp);
   }
   return rc;
 }
@@ -275,28 +330,28 @@ int disorder_connect_generic(struct config *conf,
   c->fpin = c->fpout = 0;
   if((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
     byte_xasprintf((char **)&c->last, "socket: %s", strerror(errno));
-    error(errno, "error calling socket");
+    disorder_error(errno, "error calling socket");
     return -1;
   }
   if(connect(fd, sa, salen) < 0) {
     byte_xasprintf((char **)&c->last, "connect: %s", strerror(errno));
-    error(errno, "error calling connect");
+    disorder_error(errno, "error calling connect");
     goto error;
   }
   if((fd2 = dup(fd)) < 0) {
     byte_xasprintf((char **)&c->last, "dup: %s", strerror(errno));
-    error(errno, "error calling dup");
+    disorder_error(errno, "error calling dup");
     goto error;
   }
   if(!(c->fpin = fdopen(fd, "rb"))) {
     byte_xasprintf((char **)&c->last, "fdopen: %s", strerror(errno));
-    error(errno, "error calling fdopen");
+    disorder_error(errno, "error calling fdopen");
     goto error;
   }
   fd = -1;
   if(!(c->fpout = fdopen(fd2, "wb"))) {
     byte_xasprintf((char **)&c->last, "fdopen: %s", strerror(errno));
-    error(errno, "error calling fdopen");
+    disorder_error(errno, "error calling fdopen");
     goto error;
   }
   fd2 = -1;
@@ -306,13 +361,13 @@ int disorder_connect_generic(struct config *conf,
     goto error;
   if(nrvec != 3) {
     c->last = "cannot parse server greeting";
-    error(0, "cannot parse server greeting %s", r);
+    disorder_error(0, "cannot parse server greeting %s", r);
     goto error;
   }
   protocol = *rvec++;
   if(strcmp(protocol, "2")) {
     c->last = "unknown protocol version";
-    error(0, "unknown protocol version: %s", protocol);
+    disorder_error(0, "unknown protocol version: %s", protocol);
     goto error;
   }
   algorithm = *rvec++;
@@ -325,7 +380,7 @@ int disorder_connect_generic(struct config *conf,
       return 0;                                /* success */
     if(!username) {
       c->last = "cookie failed and no username";
-      error(0, "cookie did not work and no username available");
+      disorder_error(0, "cookie did not work and no username available");
       goto error;
     }
   }
@@ -382,7 +437,7 @@ int disorder_connect(disorder_client *c) {
 
   if(!(username = config->username)) {
     c->last = "no username";
-    error(0, "no username configured");
+    disorder_error(0, "no username configured");
     return -1;
   }
   password = config->password;
@@ -396,7 +451,7 @@ int disorder_connect(disorder_client *c) {
   if(!password) {
     /* Oh well */
     c->last = "no password";
-    error(0, "no password configured");
+    disorder_error(0, "no password configured for user '%s'", username);
     return -1;
   }
   return disorder_connect_generic(config,
@@ -437,7 +492,7 @@ int disorder_close(disorder_client *c) {
   if(c->fpin) {
     if(fclose(c->fpin) < 0) {
       byte_xasprintf((char **)&c->last, "fclose: %s", strerror(errno));
-      error(errno, "error calling fclose");
+      disorder_error(errno, "error calling fclose");
       ret = -1;
     }
     c->fpin = 0;
@@ -445,7 +500,7 @@ int disorder_close(disorder_client *c) {
   if(c->fpout) {
     if(fclose(c->fpout) < 0) {
       byte_xasprintf((char **)&c->last, "fclose: %s", strerror(errno));
-      error(errno, "error calling fclose");
+      disorder_error(errno, "error calling fclose");
       ret = -1;
     }
     c->fpout = 0;
@@ -546,7 +601,7 @@ int disorder_version(disorder_client *c, char **rp) {
 
 static void client_error(const char *msg,
                         void attribute((unused)) *u) {
-  error(0, "error parsing reply: %s", msg);
+  disorder_error(0, "error parsing reply: %s", msg);
 }
 
 /** @brief Get currently playing track
@@ -596,10 +651,10 @@ static int disorder_somequeue(disorder_client *c,
   }
   if(ferror(c->fpin)) {
     byte_xasprintf((char **)&c->last, "input error: %s", strerror(errno));
-    error(errno, "error reading %s", c->ident);
+    disorder_error(errno, "error reading %s", c->ident);
   } else {
     c->last = "input error: unexpxected EOF";
-    error(0, "error reading %s: unexpected EOF", c->ident);
+    disorder_error(0, "error reading %s: unexpected EOF", c->ident);
   }
   return -1;
 }
@@ -651,10 +706,10 @@ static int readlist(disorder_client *c, char ***vecp, int *nvecp) {
   }
   if(ferror(c->fpin)) {
     byte_xasprintf((char **)&c->last, "input error: %s", strerror(errno));
-    error(errno, "error reading %s", c->ident);
+    disorder_error(errno, "error reading %s", c->ident);
   } else {
     c->last = "input error: unexpxected EOF";
-    error(0, "error reading %s: unexpected EOF", c->ident);
+    disorder_error(0, "error reading %s: unexpected EOF", c->ident);
   }
   return -1;
 }
@@ -670,6 +725,8 @@ static int readlist(disorder_client *c, char ***vecp, int *nvecp) {
  * *)0.  They should be in UTF-8.
  *
  * 5xx responses count as errors.
+ *
+ * See disorder_simple().
  */
 static int disorder_simple_list(disorder_client *c,
                                char ***vecp, int *nvecp,
@@ -678,7 +735,7 @@ static int disorder_simple_list(disorder_client *c,
   int ret;
 
   va_start(ap, cmd);
-  ret = disorder_simple_v(c, 0, cmd, ap);
+  ret = disorder_simple_v(c, 0, cmd, 0, 0, ap);
   va_end(ap);
   if(ret) return ret;
   return readlist(c, vecp, nvecp);
@@ -769,7 +826,7 @@ int disorder_get(disorder_client *c,
 
 static void pref_error_handler(const char *msg,
                               void attribute((unused)) *u) {
-  error(0, "error handling 'prefs' reply: %s", msg);
+  disorder_error(0, "error handling 'prefs' reply: %s", msg);
 }
 
 /** @brief Get all preferences for a trcak
@@ -812,7 +869,7 @@ static int boolean(const char *cmd, const char *value,
   if(!strcmp(value, "yes")) *flagp = 1;
   else if(!strcmp(value, "no")) *flagp = 0;
   else {
-    error(0, "malformed response to '%s'", cmd);
+    disorder_error(0, "malformed response to '%s'", cmd);
     return -1;
   }
   return 0;
@@ -948,7 +1005,7 @@ int disorder_get_volume(disorder_client *c, int *left, int *right) {
     return rc;
   if(sscanf(r, "%d %d", left, right) != 2) {
     c->last = "malformed volume response";
-    error(0, "error parsing response to 'volume': '%s'", r);
+    disorder_error(0, "error parsing response to 'volume': '%s'", r);
     return -1;
   }
   return 0;
@@ -1101,7 +1158,7 @@ int disorder_rtp_address(disorder_client *c, char **addressp, char **portp) {
   vec = split(r, &n, SPLIT_QUOTES, 0, 0);
   if(n != 2) {
     c->last = "malformed RTP address";
-    error(0, "malformed rtp-address reply");
+    disorder_error(0, "malformed rtp-address reply");
     return -1;
   }
   *addressp = vec[0];
@@ -1250,11 +1307,11 @@ int disorder_schedule_get(disorder_client *c, const char *id,
     return rc;
   while(*lines) {
     if(!(bits = split(*lines++, &nbits, SPLIT_QUOTES, 0, 0))) {
-      error(0, "invalid schedule-get reply: cannot split line");
+      disorder_error(0, "invalid schedule-get reply: cannot split line");
       return -1;
     }
     if(nbits != 2) {
-      error(0, "invalid schedule-get reply: wrong number of fields");
+      disorder_error(0, "invalid schedule-get reply: wrong number of fields");
       return -1;
     }
     kvp_set(actiondatap, bits[0], bits[1]);
@@ -1297,7 +1354,7 @@ int disorder_schedule_add(disorder_client *c,
                         action, key, value,
                         (char *)0);
   } else
-    fatal(0, "unknown action '%s'", action);
+    disorder_fatal(0, "unknown action '%s'", action);
   va_end(ap);
   return rc;
 }
@@ -1311,6 +1368,103 @@ int disorder_adopt(disorder_client *c, const char *id) {
   return disorder_simple(c, 0, "adopt", id, (char *)0);
 }
 
+/** @brief Delete a playlist
+ * @param c Client
+ * @param playlist Playlist to delete
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_delete(disorder_client *c,
+                             const char *playlist) {
+  return disorder_simple(c, 0, "playlist-delete", playlist, (char *)0);
+}
+
+/** @brief Get the contents of a playlist
+ * @param c Client
+ * @param playlist Playlist to get
+ * @param tracksp Where to put list of tracks
+ * @param ntracksp Where to put count of tracks
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_get(disorder_client *c, const char *playlist,
+                          char ***tracksp, int *ntracksp) {
+  return disorder_simple_list(c, tracksp, ntracksp,
+                              "playlist-get", playlist, (char *)0);
+}
+
+/** @brief List all readable playlists
+ * @param c Client
+ * @param playlistsp Where to put list of playlists
+ * @param nplaylistsp Where to put count of playlists
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlists(disorder_client *c,
+                       char ***playlistsp, int *nplaylistsp) {
+  return disorder_simple_list(c, playlistsp, nplaylistsp,
+                              "playlists", (char *)0);
+}
+
+/** @brief Get the sharing status of a playlist
+ * @param c Client
+ * @param playlist Playlist to inspect
+ * @param sharep Where to put sharing status
+ * @return 0 on success, non-0 on error
+ *
+ * Possible @p sharep values are @c public, @c private and @c shared.
+ */
+int disorder_playlist_get_share(disorder_client *c, const char *playlist,
+                                char **sharep) {
+  return disorder_simple(c, sharep,
+                         "playlist-get-share", playlist, (char *)0);
+}
+
+/** @brief Get the sharing status of a playlist
+ * @param c Client
+ * @param playlist Playlist to modify
+ * @param share New sharing status
+ * @return 0 on success, non-0 on error
+ *
+ * Possible @p share values are @c public, @c private and @c shared.
+ */
+int disorder_playlist_set_share(disorder_client *c, const char *playlist,
+                                const char *share) {
+  return disorder_simple(c, 0,
+                         "playlist-set-share", playlist, share, (char *)0);
+}
+
+/** @brief Lock a playlist for modifications
+ * @param c Client
+ * @param playlist Playlist to lock
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_lock(disorder_client *c, const char *playlist) {
+  return disorder_simple(c, 0,
+                         "playlist-lock", playlist, (char *)0);
+}
+
+/** @brief Unlock the locked playlist
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_unlock(disorder_client *c) {
+  return disorder_simple(c, 0,
+                         "playlist-unlock", (char *)0);
+}
+
+/** @brief Set the contents of a playlst
+ * @param c Client
+ * @param playlist Playlist to modify
+ * @param tracks List of tracks
+ * @param ntracks Length of @p tracks (or -1 to count up to the first NULL)
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_set(disorder_client *c,
+                          const char *playlist,
+                          char **tracks,
+                          int ntracks) {
+  return disorder_simple_body(c, 0, tracks, ntracks,
+                              "playlist-set", playlist, (char *)0);
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index 89f3037..f7ff728 100644 (file)
@@ -132,6 +132,22 @@ int disorder_schedule_add(disorder_client *c,
                          const char *action,
                          ...);
 int disorder_adopt(disorder_client *c, const char *id);
+int disorder_playlist_delete(disorder_client *c,
+                             const char *playlist);
+int disorder_playlist_get(disorder_client *c, const char *playlist,
+                          char ***tracksp, int *ntracksp);
+int disorder_playlists(disorder_client *c,
+                       char ***playlistsp, int *nplaylists);
+int disorder_playlist_get_share(disorder_client *c, const char *playlist,
+                                char **sharep);
+int disorder_playlist_set_share(disorder_client *c, const char *playlist,
+                                const char *share);
+int disorder_playlist_lock(disorder_client *c, const char *playlist);
+int disorder_playlist_unlock(disorder_client *c);
+int disorder_playlist_set(disorder_client *c,
+                          const char *playlist,
+                          char **tracks,
+                          int ntracks);
 
 #endif /* CLIENT_H */
 
index 2a0b89c..45f2329 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004-2008 Richard Kettlewell
+ * Copyright (C) 2004-2010 Richard Kettlewell
  * Portions copyright (C) 2007 Mark Wooding
  *
  * This program is free software: you can redistribute it and/or modify
 #include "inputline.h"
 #include "charset.h"
 #include "defs.h"
-#include "mixer.h"
 #include "printf.h"
 #include "regsub.h"
 #include "signame.h"
 #include "authhash.h"
 #include "vector.h"
+#include "uaudio.h"
 
 /** @brief Path to config file 
  *
- * set_configfile() sets the deafult if it is null.
+ * set_configfile() sets the default if it is null.
  */
 char *configfile;
 
@@ -62,12 +62,20 @@ char *configfile;
  */
 int config_per_user = 1;
 
+/** @brief Table of audio APIs
+ *
+ * Only set in server processes.
+ */
+const struct uaudio *const *config_uaudio_apis;
+
 /** @brief Config file parser state */
 struct config_state {
   /** @brief Filename */
   const char *path;
+
   /** @brief Line number */
   int line;
+
   /** @brief Configuration object under construction */
   struct config *config;
 };
@@ -79,22 +87,42 @@ struct config *config;
 struct conf {
   /** @brief Name as it appears in the config file */
   const char *name;
+
   /** @brief Offset in @ref config structure */
   size_t offset;
+
   /** @brief Pointer to item type */
   const struct conftype *type;
-  /** @brief Pointer to item-specific validation routine */
+
+  /** @brief Pointer to item-specific validation routine
+   * @param cs Configuration state
+   * @param nvec Length of (proposed) new value
+   * @param vec Elements of new value
+   * @return 0 on success, non-0 on error
+   *
+   * The validate function should report any error it detects.
+   */
   int (*validate)(const struct config_state *cs,
                  int nvec, char **vec);
 };
 
 /** @brief Type of a configuration item */
 struct conftype {
-  /** @brief Pointer to function to set item */
+  /** @brief Pointer to function to set item
+   * @param cs Configuration state
+   * @param whoami Configuration item to set
+   * @param nvec Length of new value
+   * @param vec New value
+   * @return 0 on success, non-0 on error
+   */
   int (*set)(const struct config_state *cs,
             const struct conf *whoami,
             int nvec, char **vec);
-  /** @brief Pointer to function to free item */
+
+  /** @brief Pointer to function to free item
+   * @param c Configuration structure to free an item of
+   * @param whoami Configuration item to free
+   */
   void (*free)(struct config *c, const struct conf *whoami);
 };
 
@@ -103,18 +131,23 @@ struct conftype {
 /** @brief Return the value of an item */
 #define VALUE(C, TYPE) (*ADDRESS(C, TYPE))
 
+static int stringlist_compare(const struct stringlist *a,
+                              const struct stringlist *b);
+static int namepartlist_compare(const struct namepartlist *a,
+                                const struct namepartlist *b);
+
 static int set_signal(const struct config_state *cs,
                      const struct conf *whoami,
                      int nvec, char **vec) {
   int n;
   
   if(nvec != 1) {
-    error(0, "%s:%d: '%s' requires one argument",
+    disorder_error(0, "%s:%d: '%s' requires one argument",
          cs->path, cs->line, whoami->name);
     return -1;
   }
   if((n = find_signal(vec[0])) == -1) {
-    error(0, "%s:%d: unknown signal '%s'",
+    disorder_error(0, "%s:%d: unknown signal '%s'",
          cs->path, cs->line, vec[0]);
     return -1;
   }
@@ -145,23 +178,23 @@ static int set_collections(const struct config_state *cs,
     root = vec[2];
     break;
   case 0:
-    error(0, "%s:%d: '%s' requires at least one argument",
-         cs->path, cs->line, whoami->name);
+    disorder_error(0, "%s:%d: '%s' requires at least one argument",
+                  cs->path, cs->line, whoami->name);
     return -1;
   default:
-    error(0, "%s:%d: '%s' requires at most three arguments",
-         cs->path, cs->line, whoami->name);
+    disorder_error(0, "%s:%d: '%s' requires at most three arguments",
+                  cs->path, cs->line, whoami->name);
     return -1;
   }
   /* Sanity check root */
   if(root[0] != '/') {
-    error(0, "%s:%d: collection root must start with '/'",
-         cs->path, cs->line);
+    disorder_error(0, "%s:%d: collection root must start with '/'",
+                  cs->path, cs->line);
     return -1;
   }
   if(root[1] && root[strlen(root)-1] == '/') {
-    error(0, "%s:%d: collection root must not end with '/'",
-         cs->path, cs->line);
+    disorder_error(0, "%s:%d: collection root must not end with '/'",
+                  cs->path, cs->line);
     return -1;
   }
   /* Defaults */
@@ -184,15 +217,15 @@ static int set_boolean(const struct config_state *cs,
   int state;
   
   if(nvec != 1) {
-    error(0, "%s:%d: '%s' takes only one argument",
-         cs->path, cs->line, whoami->name);
+    disorder_error(0, "%s:%d: '%s' takes only one argument",
+                  cs->path, cs->line, whoami->name);
     return -1;
   }
   if(!strcmp(vec[0], "yes")) state = 1;
   else if(!strcmp(vec[0], "no")) state = 0;
   else {
-    error(0, "%s:%d: argument to '%s' must be 'yes' or 'no'",
-         cs->path, cs->line, whoami->name);
+    disorder_error(0, "%s:%d: argument to '%s' must be 'yes' or 'no'",
+                  cs->path, cs->line, whoami->name);
     return -1;
   }
   VALUE(cs->config, int) = state;
@@ -203,8 +236,8 @@ static int set_string(const struct config_state *cs,
                      const struct conf *whoami,
                      int nvec, char **vec) {
   if(nvec != 1) {
-    error(0, "%s:%d: '%s' takes only one argument",
-         cs->path, cs->line, whoami->name);
+    disorder_error(0, "%s:%d: '%s' takes only one argument",
+                  cs->path, cs->line, whoami->name);
     return -1;
   }
   VALUE(cs->config, char *) = xstrdup(vec[0]);
@@ -233,16 +266,16 @@ static int set_integer(const struct config_state *cs,
   char *e;
 
   if(nvec != 1) {
-    error(0, "%s:%d: '%s' takes only one argument",
-         cs->path, cs->line, whoami->name);
+    disorder_error(0, "%s:%d: '%s' takes only one argument",
+                  cs->path, cs->line, whoami->name);
     return -1;
   }
   if(xstrtol(ADDRESS(cs->config, long), vec[0], &e, 0)) {
-    error(errno, "%s:%d: converting integer", cs->path, cs->line);
+    disorder_error(errno, "%s:%d: converting integer", cs->path, cs->line);
     return -1;
   }
   if(*e) {
-    error(0, "%s:%d: invalid integer syntax", cs->path, cs->line);
+    disorder_error(0, "%s:%d: invalid integer syntax", cs->path, cs->line);
     return -1;
   }
   return 0;
@@ -306,8 +339,8 @@ static int set_restrict(const struct config_state *cs,
 
   for(n = 0; n < nvec; ++n) {
     if((i = TABLE_FIND(restrictions, name, vec[n])) < 0) {
-      error(0, "%s:%d: invalid restriction '%s'",
-           cs->path, cs->line, vec[n]);
+      disorder_error(0, "%s:%d: invalid restriction '%s'",
+                    cs->path, cs->line, vec[n]);
       return -1;
     }
     r |= restrictions[i].bit;
@@ -323,15 +356,17 @@ static int parse_sample_format(const struct config_state *cs,
   long t;
 
   if(nvec != 1) {
-    error(0, "%s:%d: wrong number of arguments", cs->path, cs->line);
+    disorder_error(0, "%s:%d: wrong number of arguments", cs->path, cs->line);
     return -1;
   }
   if(xstrtol(&t, p, &p, 0)) {
-    error(errno, "%s:%d: converting bits-per-sample", cs->path, cs->line);
+    disorder_error(errno, "%s:%d: converting bits-per-sample",
+                  cs->path, cs->line);
     return -1;
   }
   if(t != 8 && t != 16) {
-    error(0, "%s:%d: bad bite-per-sample (%ld)", cs->path, cs->line, t);
+    disorder_error(0, "%s:%d: bad bits-per-sample (%ld)",
+                  cs->path, cs->line, t);
     return -1;
   }
   if(format) format->bits = t;
@@ -342,37 +377,38 @@ static int parse_sample_format(const struct config_state *cs,
   }
   if(format) format->endian = t;
   if(*p != '/') {
-    error(errno, "%s:%d: expected `/' after bits-per-sample",
+    disorder_error(errno, "%s:%d: expected `/' after bits-per-sample",
          cs->path, cs->line);
     return -1;
   }
   p++;
   if(xstrtol(&t, p, &p, 0)) {
-    error(errno, "%s:%d: converting sample-rate", cs->path, cs->line);
+    disorder_error(errno, "%s:%d: converting sample-rate", cs->path, cs->line);
     return -1;
   }
   if(t < 1 || t > INT_MAX) {
-    error(0, "%s:%d: silly sample-rate (%ld)", cs->path, cs->line, t);
+    disorder_error(0, "%s:%d: silly sample-rate (%ld)", cs->path, cs->line, t);
     return -1;
   }
   if(format) format->rate = t;
   if(*p != '/') {
-    error(0, "%s:%d: expected `/' after sample-rate",
-         cs->path, cs->line);
+    disorder_error(0, "%s:%d: expected `/' after sample-rate",
+                  cs->path, cs->line);
     return -1;
   }
   p++;
   if(xstrtol(&t, p, &p, 0)) {
-    error(errno, "%s:%d: converting channels", cs->path, cs->line);
+    disorder_error(errno, "%s:%d: converting channels", cs->path, cs->line);
     return -1;
   }
   if(t < 1 || t > 8) {
-    error(0, "%s:%d: silly number (%ld) of channels", cs->path, cs->line, t);
+    disorder_error(0, "%s:%d: silly number (%ld) of channels",
+                  cs->path, cs->line, t);
     return -1;
   }
   if(format) format->channels = t;
   if(*p) {
-    error(0, "%s:%d: junk after channels", cs->path, cs->line);
+    disorder_error(0, "%s:%d: junk after channels", cs->path, cs->line);
     return -1;
   }
   return 0;
@@ -395,11 +431,13 @@ static int set_namepart(const struct config_state *cs,
   pcre *re;
 
   if(nvec < 3) {
-    error(0, "%s:%d: namepart needs at least 3 arguments", cs->path, cs->line);
+    disorder_error(0, "%s:%d: namepart needs at least 3 arguments",
+                  cs->path, cs->line);
     return -1;
   }
   if(nvec > 5) {
-    error(0, "%s:%d: namepart needs at most 5 arguments", cs->path, cs->line);
+    disorder_error(0, "%s:%d: namepart needs at most 5 arguments",
+                  cs->path, cs->line);
     return -1;
   }
   reflags = nvec >= 5 ? regsub_flags(vec[4]) : 0;
@@ -407,13 +445,14 @@ static int set_namepart(const struct config_state *cs,
                         PCRE_UTF8
                         |regsub_compile_options(reflags),
                         &errstr, &erroffset, 0))) {
-    error(0, "%s:%d: error compiling regexp /%s/: %s (offset %d)",
-         cs->path, cs->line, vec[1], errstr, erroffset);
+    disorder_error(0, "%s:%d: compiling regexp /%s/: %s (offset %d)",
+                  cs->path, cs->line, vec[1], errstr, erroffset);
     return -1;
   }
   npl->s = xrealloc(npl->s, (npl->n + 1) * sizeof (struct namepart));
   npl->s[npl->n].part = xstrdup(vec[0]);
   npl->s[npl->n].re = re;
+  npl->s[npl->n].res = xstrdup(vec[1]);
   npl->s[npl->n].replace = xstrdup(vec[2]);
   npl->s[npl->n].context = xstrdup(vec[3]);
   npl->s[npl->n].reflags = reflags;
@@ -440,11 +479,13 @@ static int set_transform(const struct config_state *cs,
   int erroffset;
 
   if(nvec < 3) {
-    error(0, "%s:%d: transform needs at least 3 arguments", cs->path, cs->line);
+    disorder_error(0, "%s:%d: transform needs at least 3 arguments",
+                  cs->path, cs->line);
     return -1;
   }
   if(nvec > 5) {
-    error(0, "%s:%d: transform needs at most 5 arguments", cs->path, cs->line);
+    disorder_error(0, "%s:%d: transform needs at most 5 arguments",
+                  cs->path, cs->line);
     return -1;
   }
   reflags = (nvec >= 5 ? regsub_flags(vec[4]) : 0);
@@ -452,8 +493,8 @@ static int set_transform(const struct config_state *cs,
                         PCRE_UTF8
                         |regsub_compile_options(reflags),
                         &errstr, &erroffset, 0))) {
-    error(0, "%s:%d: error compiling regexp /%s/: %s (offset %d)",
-         cs->path, cs->line, vec[1], errstr, erroffset);
+    disorder_error(0, "%s:%d: compiling regexp /%s/: %s (offset %d)",
+                  cs->path, cs->line, vec[1], errstr, erroffset);
     return -1;
   }
   tl->t = xrealloc(tl->t, (tl->n + 1) * sizeof (struct namepart));
@@ -466,69 +507,35 @@ static int set_transform(const struct config_state *cs,
   return 0;
 }
 
-static int set_backend(const struct config_state *cs,
-                      const struct conf *whoami,
-                      int nvec, char **vec) {
-  int *const valuep = ADDRESS(cs->config, int);
-  
-  if(nvec != 1) {
-    error(0, "%s:%d: '%s' requires one argument",
-         cs->path, cs->line, whoami->name);
-    return -1;
-  }
-  if(!strcmp(vec[0], "alsa")) {
-#if HAVE_ALSA_ASOUNDLIB_H
-    *valuep = BACKEND_ALSA;
-#else
-    error(0, "%s:%d: ALSA is not available on this platform",
-         cs->path, cs->line);
-    return -1;
-#endif
-  } else if(!strcmp(vec[0], "command"))
-    *valuep = BACKEND_COMMAND;
-  else if(!strcmp(vec[0], "network"))
-    *valuep = BACKEND_NETWORK;
-  else if(!strcmp(vec[0], "coreaudio")) {
-#if HAVE_COREAUDIO_AUDIOHARDWARE_H
-    *valuep = BACKEND_COREAUDIO;
-#else
-    error(0, "%s:%d: Core Audio is not available on this platform",
-         cs->path, cs->line);
-    return -1;
-#endif
-  } else if(!strcmp(vec[0], "oss")) {
-#if HAVE_SYS_SOUNDCARD_H
-    *valuep = BACKEND_OSS;
-#else
-    error(0, "%s:%d: OSS is not available on this platform",
-         cs->path, cs->line);
-    return -1;
-#endif
-  } else {
-    error(0, "%s:%d: invalid '%s' value '%s'",
-         cs->path, cs->line, whoami->name, vec[0]);
-    return -1;
-  }
-  return 0;
-}
-
 static int set_rights(const struct config_state *cs,
                      const struct conf *whoami,
                      int nvec, char **vec) {
   if(nvec != 1) {
-    error(0, "%s:%d: '%s' requires one argument",
-         cs->path, cs->line, whoami->name);
+    disorder_error(0, "%s:%d: '%s' requires one argument",
+                  cs->path, cs->line, whoami->name);
     return -1;
   }
   if(parse_rights(vec[0], 0, 1)) {
-    error(0, "%s:%d: invalid rights string '%s'",
-         cs->path, cs->line, vec[0]);
+    disorder_error(0, "%s:%d: invalid rights string '%s'",
+                  cs->path, cs->line, vec[0]);
     return -1;
   }
   *ADDRESS(cs->config, char *) = vec[0];
   return 0;
 }
 
+static int set_netaddress(const struct config_state *cs,
+                         const struct conf *whoami,
+                         int nvec, char **vec) {
+  struct netaddress *na = ADDRESS(cs->config, struct netaddress);
+
+  if(netaddress_parse(na, nvec, vec)) {
+    disorder_error(0, "%s:%d: invalid network address", cs->path, cs->line);
+    return -1;
+  }
+  return 0;
+}
+
 /* free functions */
 
 static void free_none(struct config attribute((unused)) *c,
@@ -538,6 +545,7 @@ static void free_none(struct config attribute((unused)) *c,
 static void free_string(struct config *c,
                        const struct conf *whoami) {
   xfree(VALUE(c, char *));
+  VALUE(c, char *) = 0;
 }
 
 static void free_stringlist(struct config *c,
@@ -590,6 +598,7 @@ static void free_namepartlist(struct config *c,
     np = &npl->s[n];
     xfree(np->part);
     pcre_free(np->re);                 /* ...whatever pcre_free is set to. */
+    xfree(np->res);
     xfree(np->replace);
     xfree(np->context);
   }
@@ -612,6 +621,13 @@ static void free_transformlist(struct config *c,
   xfree(tl->t);
 }
 
+static void free_netaddress(struct config *c,
+                           const struct conf *whoami) {
+  struct netaddress *na = ADDRESS(c, struct netaddress);
+
+  xfree(na->address);
+}
+
 /* configuration types */
 
 static const struct conftype
@@ -627,155 +643,239 @@ static const struct conftype
   type_restrict = { set_restrict, free_none },
   type_namepart = { set_namepart, free_namepartlist },
   type_transform = { set_transform, free_transformlist },
-  type_rights = { set_rights, free_none },
-  type_backend = { set_backend, free_none };
+  type_netaddress = { set_netaddress, free_netaddress },
+  type_rights = { set_rights, free_none };
 
 /* specific validation routine */
 
-#define VALIDATE_FILE(test, what) do {                         \
-  struct stat sb;                                              \
-  int n;                                                       \
-                                                               \
-  for(n = 0; n < nvec; ++n) {                                  \
-    if(stat(vec[n], &sb) < 0) {                                        \
-      error(errno, "%s:%d: %s", cs->path, cs->line, vec[n]);   \
-      return -1;                                               \
-    }                                                          \
-    if(!test(sb.st_mode)) {                                    \
-      error(0, "%s:%d: %s is not a %s",                                \
-           cs->path, cs->line, vec[n], what);                  \
-      return -1;                                               \
-    }                                                          \
-  }                                                            \
+/** @brief Perform a test on a filename
+ * @param test Test function to call on mode bits
+ * @param what Type of file sought
+ *
+ * If @p test returns 0 then the file is not a @p what and an error
+ * is reported and -1 is returned.
+ */
+#define VALIDATE_FILE(test, what) do {                 \
+  struct stat sb;                                      \
+  int n;                                               \
+                                                       \
+  for(n = 0; n < nvec; ++n) {                          \
+    if(stat(vec[n], &sb) < 0) {                                \
+      disorder_error(errno, "%s:%d: %s",               \
+                     cs->path, cs->line, vec[n]);      \
+      return -1;                                       \
+    }                                                  \
+    if(!test(sb.st_mode)) {                            \
+      disorder_error(0, "%s:%d: %s is not a %s",       \
+                    cs->path, cs->line, vec[n], what); \
+      return -1;                                       \
+    }                                                  \
+  }                                                    \
 } while(0)
 
+/** @brief Validate an absolute path
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
 static int validate_isabspath(const struct config_state *cs,
                              int nvec, char **vec) {
   int n;
 
   for(n = 0; n < nvec; ++n)
     if(vec[n][0] != '/') {
-      error(errno, "%s:%d: %s: not an absolute path", 
-           cs->path, cs->line, vec[n]);
+      disorder_error(errno, "%s:%d: %s: not an absolute path", 
+                    cs->path, cs->line, vec[n]);
       return -1;
     }
   return 0;
 }
 
+/** @brief Validate an existing directory
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
 static int validate_isdir(const struct config_state *cs,
                          int nvec, char **vec) {
   VALIDATE_FILE(S_ISDIR, "directory");
   return 0;
 }
 
+/** @brief Validate an existing regular file
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
 static int validate_isreg(const struct config_state *cs,
                          int nvec, char **vec) {
   VALIDATE_FILE(S_ISREG, "regular file");
   return 0;
 }
 
+/** @brief Validate a player pattern
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
 static int validate_player(const struct config_state *cs,
                           int nvec,
                           char attribute((unused)) **vec) {
   if(nvec < 2) {
-    error(0, "%s:%d: should be at least 'player PATTERN MODULE'",
-         cs->path, cs->line);
+    disorder_error(0, "%s:%d: should be at least 'player PATTERN MODULE'",
+                  cs->path, cs->line);
     return -1;
   }
   return 0;
 }
 
+/** @brief Validate a track length pattern
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
 static int validate_tracklength(const struct config_state *cs,
                                int nvec,
                                char attribute((unused)) **vec) {
   if(nvec < 2) {
-    error(0, "%s:%d: should be at least 'tracklength PATTERN MODULE'",
-         cs->path, cs->line);
+    disorder_error(0, "%s:%d: should be at least 'tracklength PATTERN MODULE'",
+                  cs->path, cs->line);
     return -1;
   }
   return 0;
 }
 
+/** @brief Validate an allow directive
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ *
+ * Obsolete - only used for parsing legacy configuration.
+ */
 static int validate_allow(const struct config_state *cs,
                          int nvec,
                          char attribute((unused)) **vec) {
   if(nvec != 2) {
-    error(0, "%s:%d: must be 'allow NAME PASS'", cs->path, cs->line);
+    disorder_error(0, "%s:%d: must be 'allow NAME PASS'", cs->path, cs->line);
     return -1;
   }
   return 0;
 }
 
+/** @brief Validate a non-negative (@c long) integer
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
 static int validate_non_negative(const struct config_state *cs,
                                 int nvec, char **vec) {
   long n;
 
   if(nvec < 1) {
-    error(0, "%s:%d: missing argument", cs->path, cs->line);
+    disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
     return -1;
   }
   if(nvec > 1) {
-    error(0, "%s:%d: too many arguments", cs->path, cs->line);
+    disorder_error(0, "%s:%d: too many arguments", cs->path, cs->line);
     return -1;
   }
   if(xstrtol(&n, vec[0], 0, 0)) {
-    error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
+    disorder_error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
     return -1;
   }
   if(n < 0) {
-    error(0, "%s:%d: must not be negative", cs->path, cs->line);
+    disorder_error(0, "%s:%d: must not be negative", cs->path, cs->line);
     return -1;
   }
   return 0;
 }
 
+/** @brief Validate a positive (@c long) integer
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
 static int validate_positive(const struct config_state *cs,
                          int nvec, char **vec) {
   long n;
 
   if(nvec < 1) {
-    error(0, "%s:%d: missing argument", cs->path, cs->line);
+    disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
     return -1;
   }
   if(nvec > 1) {
-    error(0, "%s:%d: too many arguments", cs->path, cs->line);
+    disorder_error(0, "%s:%d: too many arguments", cs->path, cs->line);
     return -1;
   }
   if(xstrtol(&n, vec[0], 0, 0)) {
-    error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
+    disorder_error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
     return -1;
   }
   if(n <= 0) {
-    error(0, "%s:%d: must be positive", cs->path, cs->line);
+    disorder_error(0, "%s:%d: must be positive", cs->path, cs->line);
     return -1;
   }
   return 0;
 }
 
+/** @brief Validate a system username
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
 static int validate_isauser(const struct config_state *cs,
                            int attribute((unused)) nvec,
                            char **vec) {
   struct passwd *pw;
 
   if(!(pw = getpwnam(vec[0]))) {
-    error(0, "%s:%d: no such user as '%s'", cs->path, cs->line, vec[0]);
+    disorder_error(0, "%s:%d: no such user as '%s'", cs->path, cs->line, vec[0]);
     return -1;
   }
   return 0;
 }
 
+/** @brief Validate a sample format string
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
 static int validate_sample_format(const struct config_state *cs,
                                  int attribute((unused)) nvec,
                                  char **vec) {
   return parse_sample_format(cs, 0, nvec, vec);
 }
 
+/** @brief Validate anything
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0
+ */
 static int validate_any(const struct config_state attribute((unused)) *cs,
                        int attribute((unused)) nvec,
                        char attribute((unused)) **vec) {
   return 0;
 }
 
+/** @brief Validate a URL
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ *
+ * Rather cursory.
+ */
 static int validate_url(const struct config_state attribute((unused)) *cs,
                        int attribute((unused)) nvec,
                        char **vec) {
@@ -788,7 +888,7 @@ static int validate_url(const struct config_state attribute((unused)) *cs,
                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                 "0123456789"));
   if(s[n] != ':') {
-    error(0, "%s:%d: invalid url '%s'", cs->path, cs->line, vec[0]);
+    disorder_error(0, "%s:%d: invalid url '%s'", cs->path, cs->line, vec[0]);
     return -1;
   }
   if(!strncmp(s, "http:", 5)
@@ -796,13 +896,19 @@ static int validate_url(const struct config_state attribute((unused)) *cs,
     s += n + 1;
     /* we only do a rather cursory check */
     if(strncmp(s, "//", 2)) {
-      error(0, "%s:%d: invalid url '%s'", cs->path, cs->line, vec[0]);
+      disorder_error(0, "%s:%d: invalid url '%s'", cs->path, cs->line, vec[0]);
       return -1;
     }
   }
   return 0;
 }
 
+/** @brief Validate an alias pattern
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
 static int validate_alias(const struct config_state *cs,
                          int nvec,
                          char **vec) {
@@ -810,11 +916,11 @@ static int validate_alias(const struct config_state *cs,
   int in_brackets = 0, c;
 
   if(nvec < 1) {
-    error(0, "%s:%d: missing argument", cs->path, cs->line);
+    disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
     return -1;
   }
   if(nvec > 1) {
-    error(0, "%s:%d: too many arguments", cs->path, cs->line);
+    disorder_error(0, "%s:%d: too many arguments", cs->path, cs->line);
     return -1;
   }
   s = vec[0];
@@ -823,8 +929,8 @@ static int validate_alias(const struct config_state *cs,
       if(c == '}')
        in_brackets = 0;
       else if(!isalnum(c)) {
-       error(0, "%s:%d: invalid part name in alias expansion in '%s'",
-             cs->path, cs->line, vec[0]);
+       disorder_error(0, "%s:%d: invalid part name in alias expansion in '%s'",
+                      cs->path, cs->line, vec[0]);
          return -1;
       }
     } else {
@@ -834,12 +940,12 @@ static int validate_alias(const struct config_state *cs,
          ++s;
       } else if(c == '\\') {
        if(!(c = (unsigned char)*s++)) {
-         error(0, "%s:%d: unterminated escape in alias expansion in '%s'",
-               cs->path, cs->line, vec[0]);
+         disorder_error(0, "%s:%d: unterminated escape in alias expansion in '%s'",
+                        cs->path, cs->line, vec[0]);
          return -1;
        } else if(c != '\\' && c != '{') {
-         error(0, "%s:%d: invalid escape in alias expansion in '%s'",
-               cs->path, cs->line, vec[0]);
+         disorder_error(0, "%s:%d: invalid escape in alias expansion in '%s'",
+                        cs->path, cs->line, vec[0]);
          return -1;
        }
       }
@@ -847,61 +953,98 @@ static int validate_alias(const struct config_state *cs,
     ++s;
   }
   if(in_brackets) {
-    error(0, "%s:%d: unterminated part name in alias expansion in '%s'",
-         cs->path, cs->line, vec[0]);
+    disorder_error(0,
+                  "%s:%d: unterminated part name in alias expansion in '%s'",
+                  cs->path, cs->line, vec[0]);
     return -1;
   }
   return 0;
 }
 
-static int validate_addrport(const struct config_state attribute((unused)) *cs,
-                            int nvec,
-                            char attribute((unused)) **vec) {
-  switch(nvec) {
-  case 0:
-    error(0, "%s:%d: missing address",
-         cs->path, cs->line);
-    return -1;
-  case 1:
-    error(0, "%s:%d: missing port name/number",
-         cs->path, cs->line);
+/** @brief Validate a hash algorithm name
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
+static int validate_algo(const struct config_state attribute((unused)) *cs,
+                        int nvec,
+                        char **vec) {
+  if(nvec != 1) {
+    disorder_error(0, "%s:%d: invalid algorithm specification", cs->path, cs->line);
     return -1;
-  case 2:
-    return 0;
-  default:
-    error(0, "%s:%d: expected ADDRESS PORT",
-         cs->path, cs->line);
+  }
+  if(!valid_authhash(vec[0])) {
+    disorder_error(0, "%s:%d: unsuported algorithm '%s'", cs->path, cs->line, vec[0]);
     return -1;
   }
+  return 0;
 }
 
-static int validate_port(const struct config_state attribute((unused)) *cs,
-                        int nvec,
-                        char attribute((unused)) **vec) {
-  switch(nvec) {
-  case 0:
-    error(0, "%s:%d: missing address",
-         cs->path, cs->line);
+/** @brief Validate a playback backend name
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
+static int validate_backend(const struct config_state attribute((unused)) *cs,
+                            int nvec,
+                            char **vec) {
+  int n;
+  if(nvec != 1) {
+    disorder_error(0, "%s:%d: invalid sound API specification", cs->path, cs->line);
     return -1;
-  case 1:
-  case 2:
+  }
+  if(!strcmp(vec[0], "network")) {
+    disorder_error(0, "'api network' is deprecated; use 'api rtp'");
     return 0;
-  default:
-    error(0, "%s:%d: expected [ADDRESS] PORT",
-         cs->path, cs->line);
+  }
+  if(config_uaudio_apis) {
+    for(n = 0; config_uaudio_apis[n]; ++n)
+      if(!strcmp(vec[0], config_uaudio_apis[n]->name))
+        return 0;
+    disorder_error(0, "%s:%d: unrecognized sound API '%s'", cs->path, cs->line, vec[0]);
     return -1;
   }
+  /* In non-server processes we have no idea what's valid */
+  return 0;
 }
 
-static int validate_algo(const struct config_state attribute((unused)) *cs,
-                        int nvec,
-                        char **vec) {
-  if(nvec != 1) {
-    error(0, "%s:%d: invalid algorithm specification", cs->path, cs->line);
+/** @brief Validate a pause mode string
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
+static int validate_pausemode(const struct config_state attribute((unused)) *cs,
+                              int nvec,
+                              char **vec) {
+  if(nvec == 1 && (!strcmp(vec[0], "silence") || !strcmp(vec[0], "suspend")))
+    return 0;
+  disorder_error(0, "%s:%d: invalid pause mode", cs->path, cs->line);
+  return -1;
+}
+
+/** @brief Validate a destination network address
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ *
+ * By a destination address, it is meant that it must not be a wildcard
+ * address.
+ */
+static int validate_destaddr(const struct config_state attribute((unused)) *cs,
+                            int nvec,
+                            char **vec) {
+  struct netaddress na[1];
+
+  if(netaddress_parse(na, nvec, vec)) {
+    disorder_error(0, "%s:%d: invalid network address", cs->path, cs->line);
     return -1;
   }
-  if(!valid_authhash(vec[0])) {
-    error(0, "%s:%d: unsuported algorithm '%s'", cs->path, cs->line, vec[0]);
+  if(!na->address) {
+    disorder_error(0, "%s:%d: destination address required", cs->path, cs->line);
     return -1;
   }
   return 0;
@@ -916,24 +1059,24 @@ static int validate_algo(const struct config_state attribute((unused)) *cs,
 static const struct conf conf[] = {
   { C(alias),            &type_string,           validate_alias },
   { C(allow),            &type_stringlist_accum, validate_allow },
-  { C(api),              &type_backend,          validate_any },
+  { C(api),              &type_string,           validate_backend },
   { C(authorization_algorithm), &type_string,    validate_algo },
-  { C(broadcast),        &type_stringlist,       validate_addrport },
-  { C(broadcast_from),   &type_stringlist,       validate_addrport },
+  { C(broadcast),        &type_netaddress,       validate_destaddr },
+  { C(broadcast_from),   &type_netaddress,       validate_any },
   { C(channel),          &type_string,           validate_any },
   { C(checkpoint_kbyte), &type_integer,          validate_non_negative },
   { C(checkpoint_min),   &type_integer,          validate_non_negative },
   { C(collection),       &type_collections,      validate_any },
-  { C(connect),          &type_stringlist,       validate_addrport },
-  { C(cookie_login_lifetime),  &type_integer,    validate_positive },
+  { C(connect),          &type_netaddress,       validate_destaddr },
   { C(cookie_key_lifetime),  &type_integer,      validate_positive },
+  { C(cookie_login_lifetime),  &type_integer,    validate_positive },
   { C(dbversion),        &type_integer,          validate_positive },
   { C(default_rights),   &type_rights,           validate_any },
   { C(device),           &type_string,           validate_any },
   { C(gap),              &type_integer,          validate_non_negative },
   { C(history),          &type_integer,          validate_positive },
   { C(home),             &type_string,           validate_isabspath },
-  { C(listen),           &type_stringlist,       validate_port },
+  { C(listen),           &type_netaddress,       validate_any },
   { C(lock),             &type_boolean,          validate_any },
   { C(mail_sender),      &type_string,           validate_any },
   { C(mixer),            &type_string,           validate_any },
@@ -949,15 +1092,20 @@ static const struct conf conf[] = {
   { C(nice_speaker),     &type_integer,          validate_any },
   { C(noticed_history),  &type_integer,          validate_positive },
   { C(password),         &type_string,           validate_any },
+  { C(pause_mode),       &type_string,           validate_pausemode },
   { C(player),           &type_stringlist_accum, validate_player },
+  { C(playlist_lock_timeout), &type_integer,     validate_positive },
+  { C(playlist_max) ,    &type_integer,          validate_positive },
   { C(plugins),          &type_string_accum,     validate_isdir },
   { C(prefsync),         &type_integer,          validate_positive },
   { C(queue_pad),        &type_integer,          validate_positive },
-  { C(replay_min),       &type_integer,          validate_non_negative },
   { C(refresh),          &type_integer,          validate_positive },
+  { C(refresh_min),      &type_integer,          validate_non_negative },
   { C(reminder_interval), &type_integer,         validate_positive },
   { C(remote_userman),   &type_boolean,          validate_any },
+  { C(replay_min),       &type_integer,          validate_non_negative },
   { C2(restrict, restrictions),         &type_restrict,         validate_any },
+  { C(rtp_delay_threshold), &type_integer,       validate_positive },
   { C(sample_format),    &type_sample_format,    validate_sample_format },
   { C(scratch),          &type_string_accum,     validate_isreg },
   { C(sendmail),         &type_string,           validate_isabspath },
@@ -965,7 +1113,7 @@ static const struct conf conf[] = {
   { C(signal),           &type_signal,           validate_any },
   { C(smtp_server),      &type_string,           validate_any },
   { C(sox_generation),   &type_integer,          validate_non_negative },
-  { C2(speaker_backend, api),  &type_backend,          validate_any },
+  { C2(speaker_backend, api),  &type_string,     validate_backend },
   { C(speaker_command),  &type_string,           validate_any },
   { C(stopword),         &type_string_accum,     validate_any },
   { C(templates),        &type_string_accum,     validate_isdir },
@@ -986,14 +1134,21 @@ static const struct conf *find(const char *key) {
   return &conf[n];
 }
 
-/** @brief Set a new configuration value */
+/** @brief Set a new configuration value
+ * @param cs Configuration state
+ * @param nvec Length of @p vec
+ * @param vec Name and new value
+ * @return 0 on success, non-0 on error.
+ *
+ * @c vec[0] is the name, the rest is the value.
+ */
 static int config_set(const struct config_state *cs,
                      int nvec, char **vec) {
   const struct conf *which;
 
   D(("config_set %s", vec[0]));
   if(!(which = find(vec[0]))) {
-    error(0, "%s:%d: unknown configuration key '%s'",
+    disorder_error(0, "%s:%d: unknown configuration key '%s'",
          cs->path, cs->line, vec[0]);
     return -1;
   }
@@ -1001,6 +1156,12 @@ static int config_set(const struct config_state *cs,
          || which->type->set(cs, which, nvec - 1, vec + 1));
 }
 
+/** @brief Set a configuration item from parameters
+ * @param cs Configuration state
+ * @param which Item to set
+ * @param ... Value as strings, terminated by (char *)NULL
+ * @return 0 on success, non-0 on error
+ */
 static int config_set_args(const struct config_state *cs,
                           const char *which, ...) {
   va_list ap;
@@ -1017,14 +1178,21 @@ static int config_set_args(const struct config_state *cs,
   return config_set(cs, v->nvec, v->vec);
 }
 
-/** @brief Error callback used by config_include() */
+/** @brief Error callback used by config_include()
+ * @param msg Error message
+ * @param u User data (@ref config_state)
+ */
 static void config_error(const char *msg, void *u) {
   const struct config_state *cs = u;
 
-  error(0, "%s:%d: %s", cs->path, cs->line, msg);
+  disorder_error(0, "%s:%d: %s", cs->path, cs->line, msg);
 }
 
-/** @brief Include a file by name */
+/** @brief Include a file by name
+ * @param c Configuration to update
+ * @param path Path to read
+ * @return 0 on success, non-0 on error
+ */
 static int config_include(struct config *c, const char *path) {
   FILE *fp;
   char *buffer, *inputbuffer, **vec;
@@ -1036,13 +1204,13 @@ static int config_include(struct config *c, const char *path) {
   cs.config = c;
   D(("%s: reading configuration", path));
   if(!(fp = fopen(path, "r"))) {
-    error(errno, "error opening %s", path);
+    disorder_error(errno, "error opening %s", path);
     return -1;
   }
   while(!inputline(path, fp, &inputbuffer, '\n')) {
     ++cs.line;
     if(!(buffer = mb2utf8(inputbuffer))) {
-      error(errno, "%s:%d: cannot convert to UTF-8", cs.path, cs.line);
+      disorder_error(errno, "%s:%d: cannot convert to UTF-8", cs.path, cs.line);
       ret = -1;
       xfree(inputbuffer);
       continue;
@@ -1055,9 +1223,10 @@ static int config_include(struct config *c, const char *path) {
       continue;
     }
     if(n) {
+      /* 'include' is special-cased */
       if(!strcmp(vec[0], "include")) {
        if(n != 2) {
-         error(0, "%s:%d: must be 'include PATH'", cs.path, cs.line);
+         disorder_error(0, "%s:%d: must be 'include PATH'", cs.path, cs.line);
          ret = -1;
        } else
          config_include(c, vec[1]);
@@ -1069,13 +1238,14 @@ static int config_include(struct config *c, const char *path) {
     xfree(buffer);
   }
   if(ferror(fp)) {
-    error(errno, "error reading %s", path);
+    disorder_error(errno, "error reading %s", path);
     ret = -1;
   }
   fclose(fp);
   return ret;
 }
 
+/** @brief Default stopword setting */
 static const char *const default_stopwords[] = {
   "stopword",
 
@@ -1137,6 +1307,7 @@ static const char *const default_stopwords[] = {
 };
 #define NDEFAULT_STOPWORDS (sizeof default_stopwords / sizeof *default_stopwords)
 
+/** @brief Default player patterns */
 static const char *const default_players[] = {
   "*.ogg",
   "*.flac",
@@ -1145,7 +1316,9 @@ static const char *const default_players[] = {
 };
 #define NDEFAULT_PLAYERS (sizeof default_players / sizeof *default_players)
 
-/** @brief Make a new default configuration */
+/** @brief Make a new default configuration
+ * @return New configuration
+ */
 static struct config *config_default(void) {
   struct config *c = xmalloc(sizeof *c);
   const char *logname;
@@ -1161,11 +1334,12 @@ static struct config *config_default(void) {
   c->history = 60;
   c->home = xstrdup(pkgstatedir);
   if(!(pw = getpwuid(getuid())))
-    fatal(0, "cannot determine our username");
+    disorder_fatal(0, "cannot determine our username");
   logname = pw->pw_name;
   c->username = xstrdup(logname);
   c->refresh = 15;
-  c->prefsync = 3600;
+  c->refresh_min = 1;
+  c->prefsync = 0;
   c->signal = SIGKILL;
   c->alias = xstrdup("{/artist}{/album}{/title}{ext}");
   c->lock = 1;
@@ -1178,7 +1352,7 @@ static struct config *config_default(void) {
   c->sample_format.endian = ENDIAN_NATIVE;
   c->queue_pad = 10;
   c->replay_min = 8 * 3600;
-  c->api = -1;
+  c->api = NULL;
   c->multicast_ttl = 1;
   c->multicast_loop = 1;
   c->authorization_algorithm = xstrdup("sha1");
@@ -1197,6 +1371,8 @@ static struct config *config_default(void) {
   c->new_bias_age = 7 * 86400;         /* 1 week */
   c->new_bias = 4500000;               /* 50 times the base weight */
   c->sox_generation = DEFAULT_SOX_GENERATION;
+  c->playlist_max = INT_MAX;            /* effectively no limit */
+  c->playlist_lock_timeout = 10;        /* 10s */
   /* Default stopwords */
   if(config_set(&cs, (int)NDEFAULT_STOPWORDS, (char **)default_stopwords))
     exit(1);
@@ -1209,9 +1385,20 @@ static struct config *config_default(void) {
                       default_players[n], "disorder-tracklength", (char *)0))
       exit(1);
   }
+  c->broadcast.af = -1;
+  c->broadcast_from.af = -1;
+  c->listen.af = -1;
+  c->connect.af = -1;
   return c;
 }
 
+/** @brief Construct a filename
+ * @param c Configuration
+ * @param name Base filename
+ * @return Full filename
+ *
+ * Usually use config_get_file() instead.
+ */
 char *config_get_file2(struct config *c, const char *name) {
   char *s;
 
@@ -1225,7 +1412,11 @@ static void set_configfile(void) {
     byte_xasprintf(&configfile, "%s/config", pkgconfdir);
 }
 
-/** @brief Free a configuration object */
+/** @brief Free a configuration object
+ * @param c Configuration to free
+ *
+ * @p c is indeterminate after this function is called.
+ */
 static void config_free(struct config *c) {
   int n;
 
@@ -1239,7 +1430,13 @@ static void config_free(struct config *c) {
   }
 }
 
-/** @brief Set post-parse defaults */
+/** @brief Set post-parse defaults
+ * @param c Configuration to update
+ * @param server True when running in the server
+ *
+ * If @p server is set then certain parts of the configuration are more
+ * strictly validated.
+ */
 static void config_postdefaults(struct config *c,
                                int server) {
   struct config_state cs;
@@ -1277,34 +1474,36 @@ static void config_postdefaults(struct config *c,
     for(n = 0; n < NTRANSFORM; ++n)
       set_transform(&cs, whoami, 5, (char **)transform[n]);
   }
-  if(c->api == -1) {
+  if(!c->api) {
     if(c->speaker_command)
-      c->api = BACKEND_COMMAND;
-    else if(c->broadcast.n)
-      c->api = BACKEND_NETWORK;
+      c->api = xstrdup("command");
+    else if(c->broadcast.af != -1)
+      c->api = xstrdup("rtp");
+    else if(config_uaudio_apis)
+      c->api = xstrdup(config_uaudio_apis[0]->name);
     else
-      c->api = DEFAULT_BACKEND;
+      c->api = xstrdup("<none>");
   }
+  if(!strcmp(c->api, "network"))
+    c->api = xstrdup("rtp");
   if(server) {
-    if(c->api == BACKEND_COMMAND && !c->speaker_command)
-      fatal(0, "'api command' but speaker_command is not set");
-    if(c->api == BACKEND_NETWORK && !c->broadcast.n)
-      fatal(0, "'api network' but broadcast is not set");
+    if(!strcmp(c->api, "command") && !c->speaker_command)
+      disorder_fatal(0, "'api command' but speaker_command is not set");
+    if((!strcmp(c->api, "rtp")) && c->broadcast.af == -1)
+      disorder_fatal(0, "'api rtp' but broadcast is not set");
   }
   /* Override sample format */
-  switch(c->api) {
-  case BACKEND_NETWORK:
+  if(!strcmp(c->api, "rtp")) {
     c->sample_format.rate = 44100;
     c->sample_format.channels = 2;
     c->sample_format.bits = 16;
-    c->sample_format.endian = ENDIAN_BIG;
-    break;
-  case BACKEND_COREAUDIO:
+    c->sample_format.endian = ENDIAN_NATIVE;
+  }
+  if(!strcmp(c->api, "coreaudio")) {
     c->sample_format.rate = 44100;
     c->sample_format.channels = 2;
     c->sample_format.bits = 16;
     c->sample_format.endian = ENDIAN_NATIVE;
-    break; 
   }
   if(!c->default_rights) {
     rights_type r = RIGHTS__MASK & ~(RIGHT_ADMIN|RIGHT_REGISTER
@@ -1329,8 +1528,14 @@ static void config_postdefaults(struct config *c,
 
 /** @brief (Re-)read the config file
  * @param server If set, do extra checking
+ * @param oldconfig Old configuration for compatibility check
+ * @return 0 on success, non-0 on error
+ *
+ * If @p oldconfig is set, then certain compatibility checks are done between
+ * the old and new configurations.
  */
-int config_read(int server) {
+int config_read(int server,
+                const struct config *oldconfig) {
   struct config *c;
   char *privconf;
   struct passwd *pw;
@@ -1350,7 +1555,7 @@ int config_read(int server) {
   /* if there's a per-user system config file for this user, read it */
   if(config_per_user) {
     if(!(pw = getpwuid(getuid())))
-      fatal(0, "cannot determine our username");
+      disorder_fatal(0, "cannot determine our username");
     if((privconf = config_usersysconf(pw))
        && access(privconf, F_OK) == 0
        && config_include(c, privconf))
@@ -1365,15 +1570,56 @@ int config_read(int server) {
   }
   /* install default namepart and transform settings */
   config_postdefaults(c, server);
+  if(oldconfig)  {
+    int failed = 0;
+    if(strcmp(c->home, oldconfig->home)) {
+      disorder_error(0, "'home' cannot be changed without a restart");
+      failed = 1;
+    }
+    if(strcmp(c->alias, oldconfig->alias)) {
+      disorder_error(0, "'alias' cannot be changed without a restart");
+      failed = 1;
+    }
+    if(strcmp(c->user, oldconfig->user)) {
+      disorder_error(0, "'user' cannot be changed without a restart");
+      failed = 1;
+    }
+    if(c->nice_speaker != oldconfig->nice_speaker) {
+      disorder_error(0, "'nice_speaker' cannot be changed without a restart");
+      /* ...but we accept the new config anyway */
+    }
+    if(c->nice_server != oldconfig->nice_server) {
+      disorder_error(0, "'nice_server' cannot be changed without a restart");
+      /* ...but we accept the new config anyway */
+    }
+    if(namepartlist_compare(&c->namepart, &oldconfig->namepart)) {
+      disorder_error(0, "'namepart' settings cannot be changed without a restart");
+      failed = 1;
+    }
+    if(stringlist_compare(&c->stopword, &oldconfig->stopword)) {
+      disorder_error(0, "'stopword' settings cannot be changed without a restart");
+      failed = 1;
+    }
+    if(failed) {
+      disorder_error(0, "not installing incompatible new configuration");
+      return -1;
+    }
+  }
   /* everything is good so we shall use the new config */
   config_free(config);
   /* warn about obsolete directives */
   if(c->restrictions)
-    error(0, "'restrict' will be removed in a future version");
+    disorder_error(0, "'restrict' will be removed in a future version");
   if(c->allow.n)
-    error(0, "'allow' will be removed in a future version");
+    disorder_error(0, "'allow' will be removed in a future version");
   if(c->trust.n)
-    error(0, "'trust' will be removed in a future version");
+    disorder_error(0, "'trust' will be removed in a future version");
+  if(!c->lock)
+    disorder_error(0, "'lock' will be removed in a future version");
+  if(c->gap)
+    disorder_error(0, "'gap' will be removed in a future version");
+  if(c->prefsync)
+    disorder_error(0, "'prefsync' will be removed in a future version");
   config = c;
   return 0;
 }
@@ -1392,7 +1638,7 @@ char *config_userconf(const char *home, const struct passwd *pw) {
   char *s;
 
   if(!home && !pw && !(pw = getpwuid(getuid())))
-    fatal(0, "cannot determine our username");
+    disorder_fatal(0, "cannot determine our username");
   byte_xasprintf(&s, "%s/.disorder/passwd", home ? home : pw->pw_dir);
   return s;
 }
@@ -1409,10 +1655,95 @@ char *config_usersysconf(const struct passwd *pw) {
     return 0;
 }
 
+/** @brief Get a filename within the home directory
+ * @param name Relative name
+ * @return Full path
+ */
 char *config_get_file(const char *name) {
   return config_get_file2(config, name);
 }
 
+/** @brief Order two stringlists
+ * @param a First stringlist
+ * @param b Second stringlist
+ * @return <0, 0 or >0 if a<b, a=b or a>b
+ */
+static int stringlist_compare(const struct stringlist *a,
+                              const struct stringlist *b) {
+  int n = 0, c;
+
+  while(n < a->n && n < b->n) {
+    if((c = strcmp(a->s[n], b->s[n])))
+      return c;
+    ++n;
+  }
+  if(a->n < b->n)
+    return -1;
+  else if(a->n > b->n)
+    return 1;
+  else
+    return 0;
+}
+
+/** @brief Order two namepart definitions
+ * @param a First namepart definition
+ * @param b Second namepart definition
+ * @return <0, 0 or >0 if a<b, a=b or a>b
+ */
+static int namepart_compare(const struct namepart *a,
+                            const struct namepart *b) {
+  int c;
+
+  if((c = strcmp(a->part, b->part)))
+    return c;
+  if((c = strcmp(a->res, b->res)))
+    return c;
+  if((c = strcmp(a->replace, b->replace)))
+    return c;
+  if((c = strcmp(a->context, b->context)))
+    return c;
+  if(a->reflags > b->reflags)
+    return 1;
+  if(a->reflags < b->reflags)
+    return -1;
+  return 0;
+}
+
+/** @brief Order two lists of namepart definitions
+ * @param a First list of namepart definitions
+ * @param b Second list of namepart definitions
+ * @return <0, 0 or >0 if a<b, a=b or a>b
+ */
+static int namepartlist_compare(const struct namepartlist *a,
+                                const struct namepartlist *b) {
+  int n = 0, c;
+
+  while(n < a->n && n < b->n) {
+    if((c = namepart_compare(&a->s[n], &b->s[n])))
+      return c;
+    ++n;
+  }
+  if(a->n > b->n)
+    return 1;
+  else if(a->n < b->n)
+    return -1;
+  else
+    return 0;
+}
+
+/** @brief Verify configuration table.
+ * @return The number of problems found
+*/
+int config_verify(void) {
+  int fails = 0;
+  for(size_t n = 1; n < sizeof conf / sizeof *conf; ++n)
+    if(strcmp(conf[n-1].name, conf[n].name) >= 0) {
+      fprintf(stderr, "%s >= %s\n", conf[n-1].name, conf[n].name);
+      ++fails;
+    }
+  return fails;
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index 624e495..617d929 100644 (file)
@@ -1,7 +1,6 @@
-
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004-2008 Richard Kettlewell
+ * Copyright (C) 2004-2010 Richard Kettlewell
  *
  * 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
@@ -27,6 +26,9 @@
 
 #include "speaker-protocol.h"
 #include "rights.h"
+#include "addr.h"
+
+struct uaudio;
 
 /* Configuration is kept in a @struct config@; the live configuration
  * is always pointed to by @config@.  Values in @config@ are UTF-8 encoded.
@@ -68,7 +70,8 @@ struct collectionlist {
 
 struct namepart {
   char *part;                          /* part */
-  pcre *re;                            /* regexp */
+  pcre *re;                            /* compiled regexp */
+  char *res;                            /* regexp as a string */
   char *replace;                       /* replacement string */
   char *context;                       /* context glob */
   unsigned reflags;                    /* regexp flags */
@@ -153,7 +156,7 @@ struct config {
   long prefsync;                       /* preflog sync interval */
 
   /** @brief Secondary listen address */
-  struct stringlist listen;
+  struct netaddress listen;
 
   /** @brief Alias format string */
   const char *alias;
@@ -170,35 +173,23 @@ struct config {
   /** @brief Command execute by speaker to play audio */
   const char *speaker_command;
 
+  /** @brief Pause mode for command backend */
+  const char *pause_mode;
+  
   /** @brief Target sample format */
   struct stream_header sample_format;
 
   /** @brief Sox syntax generation */
   long sox_generation;
 
-  /** @brief API used to play sound
-   *
-   * Choices are @ref BACKEND_ALSA, @ref BACKEND_COMMAND or @ref
-   * BACKEND_NETWORK.
-   */
-  int api;
-
-/* These values had better be non-negative */
-#define BACKEND_ALSA 0                 /**< Use ALSA (Linux only) */
-#define BACKEND_COMMAND 1              /**< Execute a command */
-#define BACKEND_NETWORK 2              /**< Transmit RTP  */
-#define BACKEND_COREAUDIO 3            /**< Use Core Audio (Mac only) */
-#define BACKEND_OSS 4                  /**< Use OSS */
-
-#if HAVE_ALSA_ASOUNDLIB_H
-# define DEFAULT_BACKEND BACKEND_ALSA
-#elif HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
-# define DEFAULT_BACKEND BACKEND_OSS
-#elif HAVE_COREAUDIO_AUDIOHARDWARE_H
-# define DEFAULT_BACKEND BACKEND_COREAUDIO
-#else
-# error Cannot choose a default backend
-#endif
+  /** @brief API used to play sound */
+  const char *api;
+
+  /** @brief Maximum size of a playlist */
+  long playlist_max;
+
+  /** @brief Maximum lifetime of a playlist lock */
+  long playlist_lock_timeout;
 
   /** @brief Home directory for state files */
   const char *home;
@@ -210,7 +201,7 @@ struct config {
   const char *password;
 
   /** @brief Address to connect to */
-  struct stringlist connect;
+  struct netaddress connect;
 
   /** @brief Directories to search for web templates */
   struct stringlist templates;
@@ -224,6 +215,9 @@ struct config {
   /** @brief Maximum refresh interval for web interface (seconds) */
   long refresh;
 
+  /** @brief Minimum refresh interval for web interface (seconds) */
+  long refresh_min;
+
   /** @brief Facilities restricted to trusted users
    *
    * A bitmap of @ref RESTRICT_SCRATCH, @ref RESTRICT_REMOVE and @ref
@@ -250,11 +244,14 @@ struct config {
   struct transformlist transform;      /* path name transformations */
 
   /** @brief Address to send audio data to */
-  struct stringlist broadcast;
+  struct netaddress broadcast;
 
   /** @brief Source address for network audio transmission */
-  struct stringlist broadcast_from;
+  struct netaddress broadcast_from;
 
+  /** @brief RTP delay threshold */
+  long rtp_delay_threshold;
+  
   /** @brief TTL for multicast packets */
   long multicast_ttl;
 
@@ -305,7 +302,8 @@ struct config {
 extern struct config *config;
 /* the current configuration */
 
-int config_read(int server);
+int config_read(int server,
+                const struct config *oldconfig);
 /* re-read config, return 0 on success or non-0 on error.
  * Only updates @config@ if the new configuration is valid. */
 
@@ -325,9 +323,13 @@ char *config_usersysconf(const struct passwd *pw );
 char *config_private(void);
 /* get the private config file */
 
+int config_verify(void);
+
 extern char *configfile;
 extern int config_per_user;
 
+extern const struct uaudio *const *config_uaudio_apis;
+
 #endif /* CONFIGURATION_H */
 
 /*
index 0ff17b5..0efe8d0 100644 (file)
@@ -35,6 +35,7 @@
 #include "configuration.h"
 #include "kvp.h"
 #include "trackdb.h"
+#include "syscalls.h"
 
 /** @brief Hash function used in signing HMAC */
 #define ALGO GCRY_MD_SHA1
@@ -66,7 +67,7 @@ static int revoked_cleanup_callback(const char *key, void *value,
 static void newkey(void) {
   time_t now;
 
-  time(&now);
+  xtime(&now);
   memcpy(old_signing_key, signing_key, HASHSIZE);
   gcry_randomize(signing_key, HASHSIZE, GCRY_STRONG_RANDOM);
   signing_key_validity_limit = now + config->cookie_key_lifetime;
@@ -98,11 +99,11 @@ static char *sign(const uint8_t *key,
   char *sig64;
 
   if((e = gcry_md_open(&h, ALGO, GCRY_MD_FLAG_HMAC))) {
-    error(0, "gcry_md_open: %s", gcry_strerror(e));
+    disorder_error(0, "gcry_md_open: %s", gcry_strerror(e));
     return 0;
   }
   if((e = gcry_md_setkey(h, key, HASHSIZE))) {
-    error(0, "gcry_md_setkey: %s", gcry_strerror(e));
+    disorder_error(0, "gcry_md_setkey: %s", gcry_strerror(e));
     gcry_md_close(h);
     return 0;
   }
@@ -124,17 +125,17 @@ char *make_cookie(const char *user) {
 
   /* dollar signs aren't allowed in usernames */
   if(strchr(user, '$')) {
-    error(0, "make_cookie for username with dollar sign");
+    disorder_error(0, "make_cookie for username with dollar sign");
     return 0;
   }
   /* look up the password */
   password = trackdb_get_password(user);
   if(!password) {
-    error(0, "make_cookie for nonexistent user");
+    disorder_error(0, "make_cookie for nonexistent user");
     return 0;
   }
   /* make sure we have a valid signing key */
-  time(&now);
+  xtime(&now);
   if(now >= signing_key_validity_limit)
     newkey();
   /* construct the subject */
@@ -164,38 +165,38 @@ char *verify_cookie(const char *cookie, rights_type *rights) {
 
   /* check the revocation list */
   if(revoked && hash_find(revoked, cookie)) {
-    error(0, "attempt to log in with revoked cookie");
+    disorder_error(0, "attempt to log in with revoked cookie");
     return 0;
   }
   /* parse the cookie */
   errno = 0;
   t = strtoimax(cookie, &c1, 16);
   if(errno) {
-    error(errno, "error parsing cookie timestamp");
+    disorder_error(errno, "error parsing cookie timestamp");
     return 0;
   }
   if(*c1 != '$') {
-    error(0, "invalid cookie timestamp");
+    disorder_error(0, "invalid cookie timestamp");
     return 0;
   }
   /* There'd better be two dollar signs */
   c2 = strchr(c1 + 1, '$');
   if(c2 == 0) {
-    error(0, "invalid cookie syntax");
+    disorder_error(0, "invalid cookie syntax");
     return 0;
   }
   /* Extract the username */
   user = xstrndup(c1 + 1, c2 - (c1 + 1));
   /* check expiry */
-  time(&now);
+  xtime(&now);
   if(now >= t) {
-    error(0, "cookie has expired");
+    disorder_error(0, "cookie has expired");
     return 0;
   }
   /* look up the password */
   k = trackdb_getuserinfo(user);
   if(!k) {
-    error(0, "verify_cookie for nonexistent user");
+    disorder_error(0, "verify_cookie for nonexistent user");
     return 0;
   }
   password = kvp_get(k, "password");
@@ -217,7 +218,7 @@ char *verify_cookie(const char *cookie, rights_type *rights) {
   if(!strcmp(sig, c2 + 1))
     return user;
   /* that didn't match either */
-  error(0, "cookie signature does not match");
+  disorder_error(0, "cookie signature does not match");
   return 0;
 }
 
diff --git a/lib/coreaudio.c b/lib/coreaudio.c
new file mode 100644 (file)
index 0000000..aafab86
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/coreaudio.c
+ * @brief Support for Apple Core Audio
+ */
+
+#include "common.h"
+
+#if HAVE_COREAUDIO_AUDIOHARDWARE_H
+
+#include "coreaudio.h"
+#include "log.h"
+#include "printf.h"
+#include <CoreFoundation/CFString.h>
+/* evil bodge to work around broken header file */
+#undef error
+#include <CoreServices/CoreServices.h>
+#include <stdarg.h>
+
+/** @brief Report an error with an OSStatus */
+void coreaudio_fatal(OSStatus err, const char *fmt, ...) {
+  va_list ap;
+  char *msg;
+
+  va_start(ap, fmt);
+  byte_vasprintf(&msg, fmt, ap);
+  va_end(ap);
+
+  disorder_fatal(0, "%s: error %u", msg, (unsigned)err);
+}
+
+/** @brief Return the default device ID */
+static AudioDeviceID coreaudio_use_default(void) {
+  OSStatus status;
+  UInt32 propertySize;
+  AudioDeviceID adid;
+
+  /* TODO should we use kAudioHardwarePropertyDefaultSystemOutputDevice
+   * instead?  It is listed in the enumeration but is not documented, so we
+   * leave it alone until better information is available. */
+  propertySize = sizeof adid;
+  status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
+                                    &propertySize, &adid);
+  if(status)
+    coreaudio_fatal(status, "AudioHardwareGetProperty kAudioHardwarePropertyDefaultOutputDevice");
+  if(adid == kAudioDeviceUnknown)
+    disorder_fatal(0, "no output device");
+  return adid;
+}
+
+/** @brief Find a device by some string
+ * @param selector Selector for property to look for
+ * @param devs List of device IDs
+ * @param ndevs Number of device IDs in @p devs
+ * @param dev Desired device name
+ * @param resultp Where to put device ID
+ * @return 1 if found, 0 if not found
+ */
+static int coreaudio_find_device(AudioObjectPropertySelector selector,
+                                 //const char *description,
+                                 const AudioDeviceID *devs,
+                                 unsigned ndevs,
+                                 CFStringRef dev,
+                                 AudioDeviceID *resultp) {
+  OSStatus status;
+  UInt32 propertySize;
+
+  for(unsigned n = 0; n < ndevs; ++n) {
+    CFStringRef name;
+    propertySize = sizeof name;
+    status = AudioDeviceGetProperty(devs[n], 0, FALSE,
+                                    selector,
+                                    &propertySize, &name);
+    if(status)
+      coreaudio_fatal(status, "AudioDeviceGetProperty");
+#if 0
+    UInt8 output[1024];
+    CFIndex used;
+    CFRange r = { 0, CFStringGetLength(name) };
+    CFStringGetBytes(name, r, kCFStringEncodingUTF8,
+                     '?', FALSE, output, sizeof output,
+                     &used);
+    output[used] = 0;
+    disorder_info("device %u %s: %s", n, description, (char *)output);
+#endif
+    if(CFStringCompare(dev, name,
+                       kCFCompareCaseInsensitive|kCFCompareNonliteral)
+       == kCFCompareEqualTo) {
+      *resultp = devs[n];
+      CFRelease(name);
+      return 1;
+    }
+    CFRelease(name);
+  }
+  return 0;                             /* didn't find it */
+}
+
+/** @brief Identify audio device
+ * @param name Device name
+ * @return Device ID
+ */
+AudioDeviceID coreaudio_getdevice(const char *name) {
+  OSStatus status;
+  UInt32 propertySize;
+  int found;
+  AudioDeviceID adid;
+
+  if(!name
+     || !*name
+     || !strcmp(name, "default"))
+    return coreaudio_use_default();
+  /* Convert the configured device name to a CFString */
+  CFStringRef dev;
+  dev = CFStringCreateWithCString(NULL/*default allocator*/,
+                                  name,
+                                  kCFStringEncodingUTF8);
+  if(!dev)
+    disorder_fatal(0, "CFStringCreateWithBytes failed");
+  /* Get a list of available devices */
+  AudioDeviceID devs[1024];
+  unsigned ndevs;
+
+  propertySize = sizeof devs;
+  status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
+                                    &propertySize, devs);
+  if(status)
+    coreaudio_fatal(status, "AudioHardwareGetProperty kAudioHardwarePropertyDevices");
+  ndevs = propertySize / sizeof *devs;
+  if(!ndevs)
+    disorder_fatal(0, "no sound devices found");
+  /* Try looking up by UID first */
+  found = coreaudio_find_device(-1*kAudioDevicePropertyDeviceUID, //"UID",
+                                devs, ndevs, dev, &adid);
+  /* Failing that try looking up by name */
+  if(!found)
+    found = coreaudio_find_device(kAudioObjectPropertyName, //"name",
+                                  devs, ndevs, dev, &adid);
+  CFRelease(dev);
+  if(!found)
+    disorder_fatal(0, "cannot find device '%s'", name);
+  return adid;
+}
+
+#endif
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
similarity index 64%
rename from lib/mem-impl.h
rename to lib/coreaudio.h
index b3a16bb..706f609 100644 (file)
@@ -1,12 +1,12 @@
 /*
- * This file is part of DisOrder.
- * Copyright (C) 2004, 2007, 2008 Richard Kettlewell
+ * This file is part of DisOrder
+ * Copyright (C) 2009 Richard Kettlewell
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
+/** @file lib/coreaudio.h
+ * @brief Support for Apple Core Audio
+ */
+
+#ifndef COREAUDIO_H
+#define COREAUDIO_H
+
+#include <CoreAudio/AudioHardware.h>
 
-int disorder_asprintf(char **rp, const char *fmt, ...) {
-  va_list ap;
-  int n;
+void coreaudio_fatal(OSStatus err, const char *fmt, ...);
+AudioDeviceID coreaudio_getdevice(const char *name);
 
-  va_start(ap, fmt);
-  n = byte_vasprintf(rp, fmt, ap);
-  va_end(ap);
-  return n;
-}
+#endif /* COREAUDIO_H */
 
 /*
 Local Variables:
 c-basic-offset:2
 comment-column:40
+fill-column:79
+indent-tabs-mode:nil
 End:
 */
index be96e8f..a620f81 100644 (file)
@@ -62,11 +62,11 @@ time_t dateparse(const char *s) {
   case 0:
     return mktime(&t);
   case 7:
-    fatal(0, "date string '%s' not in a recognized format", s);
+    disorder_fatal(0, "date string '%s' not in a recognized format", s);
   case 8:
-    fatal(0, "date string '%s' not representable", s);
+    disorder_fatal(0, "date string '%s' not representable", s);
   default:
-    fatal(0, "date string '%s' produced unexpected error %d", s, rc);
+    disorder_fatal(0, "date string '%s' produced unexpected error %d", s, rc);
   }
 }
 
index 633d56a..f0e35b2 100644 (file)
@@ -41,6 +41,9 @@ const char pkgstatedir[] = PKGSTATEDIR;
 /** @brief Package fixed data directory */
 const char pkgdatadir[] = PKGDATADIR;
 
+/** @brief Package HTML documentation directory */
+const char dochtmldir[] = DOCHTMLDIR;
+
 /** @brief Binary directory */
 const char bindir[] = BINDIR;
 
index 41b65ea..e13b857 100644 (file)
@@ -25,6 +25,7 @@ extern const char pkglibdir[];
 extern const char pkgconfdir[];
 extern const char pkgstatedir[];
 extern const char pkgdatadir[];
+extern const char dochtmldir[];
 extern const char bindir[];
 extern const char sbindir[];
 extern const char finkbindir[];
index 40639f3..5eda674 100644 (file)
@@ -92,6 +92,7 @@ typedef void operation_callback(disorder_eclient *c, struct operation *op);
 struct operation {
   struct operation *next;          /**< @brief next operation */
   char *cmd;                       /**< @brief command to send or 0 */
+  char **body;                     /**< @brief command body */
   operation_callback *opcallback;  /**< @brief internal completion callback */
   void (*completed)();             /**< @brief user completion callback or 0 */
   void *v;                         /**< @brief data for COMPLETED */
@@ -165,6 +166,8 @@ static void stash_command(disorder_eclient *c,
                           operation_callback *opcallback,
                           void (*completed)(),
                           void *v,
+                          int nbody,
+                          char **body,
                           const char *cmd,
                           ...);
 static void log_opcallback(disorder_eclient *c, struct operation *op);
@@ -187,6 +190,9 @@ static void logentry_user_delete(disorder_eclient *c, int nvec, char **vec);
 static void logentry_user_edit(disorder_eclient *c, int nvec, char **vec);
 static void logentry_rights_changed(disorder_eclient *c, int nvec, char **vec);
 static void logentry_adopted(disorder_eclient *c, int nvec, char **vec);
+static void logentry_playlist_created(disorder_eclient *c, int nvec, char **vec);
+static void logentry_playlist_deleted(disorder_eclient *c, int nvec, char **vec);
+static void logentry_playlist_modified(disorder_eclient *c, int nvec, char **vec);
 
 /* Tables ********************************************************************/
 
@@ -208,6 +214,9 @@ static const struct logentry_handler logentry_handlers[] = {
   LE(failed, 2, 2),
   LE(moved, 1, 1),
   LE(playing, 1, 2),
+  LE(playlist_created, 2, 2),
+  LE(playlist_deleted, 1, 1),
+  LE(playlist_modified, 2, 2),
   LE(queue, 2, INT_MAX),
   LE(recent_added, 2, INT_MAX),
   LE(recent_removed, 1, 1),
@@ -326,6 +335,24 @@ static int protocol_error(disorder_eclient *c, struct operation *op,
 
 /* State machine *************************************************************/
 
+/** @brief Send an operation (into the output buffer)
+ * @param op Operation to send
+ */
+static void op_send(struct operation *op) {
+  disorder_eclient *const c = op->client;
+  put(c, op->cmd, strlen(op->cmd));
+  if(op->body) {
+    for(int n = 0; op->body[n]; ++n) {
+      if(op->body[n][0] == '.')
+        put(c, ".", 1);
+      put(c, op->body[n], strlen(op->body[n]));
+      put(c, "\n", 1);
+    }
+    put(c, ".\n", 2);
+  }
+  op->sent = 1;
+}
+
 /** @brief Called when there's something to do
  * @param c Client
  * @param mode bitmap of @ref DISORDER_POLL_READ and/or @ref DISORDER_POLL_WRITE.
@@ -379,7 +406,7 @@ void disorder_eclient_polled(disorder_eclient *c, unsigned mode) {
     D(("state_connected"));
     /* We just connected.  Initiate the authentication protocol. */
     stash_command(c, 1/*queuejump*/, authbanner_opcallback,
-                  0/*completed*/, 0/*v*/, 0/*cmd*/);
+                  0/*completed*/, 0/*v*/, -1/*nbody*/, 0/*body*/, 0/*cmd*/);
     /* We never stay is state_connected very long.  We could in principle jump
      * straight to state_cmdresponse since there's actually no command to
      * send, but that would arguably be cheating. */
@@ -395,17 +422,13 @@ void disorder_eclient_polled(disorder_eclient *c, unsigned mode) {
       if(c->authenticated) {
         /* Transmit all unsent operations */
         for(op = c->ops; op; op = op->next) {
-          if(!op->sent) {
-            put(c, op->cmd, strlen(op->cmd));
-            op->sent = 1;
-          }
+          if(!op->sent)
+            op_send(op);
         }
       } else {
         /* Just send the head operation */
-        if(c->ops->cmd && !c->ops->sent) {
-          put(c, c->ops->cmd, strlen(c->ops->cmd));
-          c->ops->sent = 1;
-        }
+        if(c->ops->cmd && !c->ops->sent)
+          op_send(c->ops);
       }
       /* Awaiting response for the operation at the head of the list */
       c->state = state_cmdresponse;
@@ -417,7 +440,7 @@ void disorder_eclient_polled(disorder_eclient *c, unsigned mode) {
   /* Queue up a byte to send */
   if(c->state == state_log
      && c->output.nvec == 0
-     && time(&now) - c->last_prod > LOG_PROD_INTERVAL) {
+     && xtime(&now) - c->last_prod > LOG_PROD_INTERVAL) {
     put(c, "x", 1);
     c->last_prod = now;
   }
@@ -601,6 +624,7 @@ static void authbanner_opcallback(disorder_eclient *c,
     return;
   }
   stash_command(c, 1/*queuejump*/, authuser_opcallback, 0/*completed*/, 0/*v*/,
+                -1/*nbody*/, 0/*body*/,
                 "user", quoteutf8(config->username), quoteutf8(res),
                 (char *)0);
 }
@@ -625,6 +649,7 @@ static void authuser_opcallback(disorder_eclient *c,
   if(c->log_callbacks && !(c->ops && c->ops->opcallback == log_opcallback))
     /* We are a log client, switch to logging mode */
     stash_command(c, 0/*queuejump*/, log_opcallback, 0/*completed*/, c->log_v,
+                  -1/*nbody*/, 0/*body*/,
                   "log", (char *)0);
 }
 
@@ -716,7 +741,7 @@ static void process_line(disorder_eclient *c, char *line) {
          && line[1] >= '0' && line[1] <= '9'
          && line[2] >= '0' && line[2] <= '9'
          && line[3] == ' '))
-      fatal(0, "invalid response from server: %s", line);
+      disorder_fatal(0, "invalid response from server: %s", line);
     c->rc = (line[0] * 10 + line[1]) * 10 + line[2] - 111 * '0';
     c->line = line;
     switch(c->rc % 10) {
@@ -787,6 +812,8 @@ static void stash_command_vector(disorder_eclient *c,
                                  operation_callback *opcallback,
                                  void (*completed)(),
                                  void *v,
+                                 int nbody,
+                                 char **body,
                                  int ncmd,
                                  char **cmd) {
   struct operation *op = xmalloc(sizeof *op);
@@ -805,6 +832,13 @@ static void stash_command_vector(disorder_eclient *c,
     op->cmd = d.vec;
   } else
     op->cmd = 0;                        /* usually, awaiting challenge */
+  if(nbody >= 0) {
+    op->body = xcalloc(nbody + 1, sizeof (char *));
+    for(n = 0; n < nbody; ++n)
+      op->body[n] = xstrdup(body[n]);
+    op->body[n] = 0;
+  } else
+    op->body = NULL;
   op->opcallback = opcallback;
   op->completed = completed;
   op->v = v;
@@ -830,6 +864,8 @@ static void vstash_command(disorder_eclient *c,
                            operation_callback *opcallback,
                            void (*completed)(),
                            void *v,
+                           int nbody,
+                           char **body,
                            const char *cmd, va_list ap) {
   char *arg;
   struct vector vec;
@@ -841,9 +877,11 @@ static void vstash_command(disorder_eclient *c,
     while((arg = va_arg(ap, char *)))
       vector_append(&vec, arg);
     stash_command_vector(c, queuejump, opcallback, completed, v, 
-                         vec.nvec, vec.vec);
+                         nbody, body, vec.nvec, vec.vec);
   } else
-    stash_command_vector(c, queuejump, opcallback, completed, v, 0, 0);
+    stash_command_vector(c, queuejump, opcallback, completed, v,
+                         nbody, body,
+                         0, 0);
 }
 
 static void stash_command(disorder_eclient *c,
@@ -851,12 +889,14 @@ static void stash_command(disorder_eclient *c,
                           operation_callback *opcallback,
                           void (*completed)(),
                           void *v,
+                          int nbody,
+                          char **body,
                           const char *cmd,
                           ...) {
   va_list ap;
 
   va_start(ap, cmd);
-  vstash_command(c, queuejump, opcallback, completed, v, cmd, ap);
+  vstash_command(c, queuejump, opcallback, completed, v, nbody, body, cmd, ap);
   va_end(ap);
 }
 
@@ -1008,6 +1048,8 @@ static void list_response_opcallback(disorder_eclient *c,
   D(("list_response_callback"));
   if(c->rc / 100 == 2)
     completed(op->v, NULL, c->vec.nvec, c->vec.vec);
+  else if(c->rc == 555)
+    completed(op->v, NULL, -1, NULL);
   else
     completed(op->v, errorstring(c), 0, 0);
 }
@@ -1039,7 +1081,24 @@ static int simple(disorder_eclient *c,
   va_list ap;
 
   va_start(ap, cmd);
-  vstash_command(c, 0/*queuejump*/, opcallback, completed, v, cmd, ap);
+  vstash_command(c, 0/*queuejump*/, opcallback, completed, v, -1, 0, cmd, ap);
+  va_end(ap);
+  /* Give the state machine a kick, since we might be in state_idle */
+  disorder_eclient_polled(c, 0);
+  return 0;
+}
+
+static int simple_body(disorder_eclient *c,
+                       operation_callback *opcallback,
+                       void (*completed)(),
+                       void *v,
+                       int nbody,
+                       char **body,
+                       const char *cmd, ...) {
+  va_list ap;
+
+  va_start(ap, cmd);
+  vstash_command(c, 0/*queuejump*/, opcallback, completed, v, nbody, body, cmd, ap);
   va_end(ap);
   /* Give the state machine a kick, since we might be in state_idle */
   disorder_eclient_polled(c, 0);
@@ -1073,6 +1132,28 @@ int disorder_eclient_play(disorder_eclient *c,
                 "play", track, (char *)0);
 }
 
+int disorder_eclient_playafter(disorder_eclient *c,
+                               const char *target,
+                               int ntracks,
+                               const char **tracks,
+                               disorder_eclient_no_response *completed,
+                               void *v) {
+  struct vector vec;
+  int n;
+
+  if(!target)
+    target = "";
+  vector_init(&vec);
+  vector_append(&vec, (char *)"playafter");
+  vector_append(&vec, (char *)target);
+  for(n = 0; n < ntracks; ++n)
+    vector_append(&vec, (char *)tracks[n]);
+  stash_command_vector(c, 0/*queuejump*/, no_response_opcallback, completed, v,
+                       -1, 0, vec.nvec, vec.vec);
+  disorder_eclient_polled(c, 0);
+  return 0;
+}
+
 int disorder_eclient_pause(disorder_eclient *c,
                            disorder_eclient_no_response *completed,
                            void *v) {
@@ -1124,7 +1205,7 @@ int disorder_eclient_moveafter(disorder_eclient *c,
   for(n = 0; n < nids; ++n)
     vector_append(&vec, (char *)ids[n]);
   stash_command_vector(c, 0/*queuejump*/, no_response_opcallback, completed, v,
-                       vec.nvec, vec.vec);
+                       -1, 0, vec.nvec, vec.vec);
   disorder_eclient_polled(c, 0);
   return 0;
 }
@@ -1420,6 +1501,123 @@ int disorder_eclient_adopt(disorder_eclient *c,
                 "adopt", id, (char *)0);
 }
 
+/** @brief Get the list of playlists
+ * @param c Client
+ * @param completed Called with list of playlists
+ * @param v Passed to @p completed
+ *
+ * The playlist list is not sorted in any particular order.
+ */
+int disorder_eclient_playlists(disorder_eclient *c,
+                               disorder_eclient_list_response *completed,
+                               void *v) {
+  return simple(c, list_response_opcallback, (void (*)())completed, v,
+                "playlists", (char *)0);
+}
+
+/** @brief Delete a playlist
+ * @param c Client
+ * @param completed Called on completion
+ * @param playlist Playlist to delete
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_delete(disorder_eclient *c,
+                                     disorder_eclient_no_response *completed,
+                                     const char *playlist,
+                                     void *v) {
+  return simple(c, no_response_opcallback,  (void (*)())completed, v,
+                "playlist-delete", playlist, (char *)0);
+}
+
+/** @brief Lock a playlist
+ * @param c Client
+ * @param completed Called on completion
+ * @param playlist Playlist to lock
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_lock(disorder_eclient *c,
+                                   disorder_eclient_no_response *completed,
+                                   const char *playlist,
+                                   void *v) {
+  return simple(c, no_response_opcallback,  (void (*)())completed, v,
+                "playlist-lock", playlist, (char *)0);
+}
+
+/** @brief Unlock the locked a playlist
+ * @param c Client
+ * @param completed Called on completion
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_unlock(disorder_eclient *c,
+                                     disorder_eclient_no_response *completed,
+                                     void *v) {
+  return simple(c, no_response_opcallback,  (void (*)())completed, v,
+                "playlist-unlock", (char *)0);
+}
+
+/** @brief Set a playlist's sharing
+ * @param c Client
+ * @param completed Called on completion
+ * @param playlist Playlist to modify
+ * @param sharing @c "public" or @c "private"
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_set_share(disorder_eclient *c,
+                                        disorder_eclient_no_response *completed,
+                                        const char *playlist,
+                                        const char *sharing,
+                                        void *v) {
+  return simple(c, no_response_opcallback,  (void (*)())completed, v,
+                "playlist-set-share", playlist, sharing, (char *)0);
+}
+
+/** @brief Get a playlist's sharing
+ * @param c Client
+ * @param completed Called with sharing status
+ * @param playlist Playlist to inspect
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_get_share(disorder_eclient *c,
+                                        disorder_eclient_string_response *completed,
+                                        const char *playlist,
+                                        void *v) {
+  return simple(c, string_response_opcallback,  (void (*)())completed, v,
+                "playlist-get-share", playlist, (char *)0);
+}
+
+/** @brief Set a playlist
+ * @param c Client
+ * @param completed Called on completion
+ * @param playlist Playlist to modify
+ * @param tracks List of tracks
+ * @param ntracks Number of tracks
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_set(disorder_eclient *c,
+                                  disorder_eclient_no_response *completed,
+                                  const char *playlist,
+                                  char **tracks,
+                                  int ntracks,
+                                  void *v) {
+  return simple_body(c, no_response_opcallback, (void (*)())completed, v,
+                     ntracks, tracks,
+                     "playlist-set", playlist, (char *)0);
+}
+
+/** @brief Get a playlist's contents
+ * @param c Client
+ * @param completed Called with playlist contents
+ * @param playlist Playlist to inspect
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_get(disorder_eclient *c,
+                                  disorder_eclient_list_response *completed,
+                                  const char *playlist,
+                                  void *v) {
+  return simple(c, list_response_opcallback,  (void (*)())completed, v,
+                "playlist-get", playlist, (char *)0);
+}
+
 /* Log clients ***************************************************************/
 
 /** @brief Monitor the server log
@@ -1444,7 +1642,7 @@ int disorder_eclient_log(disorder_eclient *c,
   if(c->log_callbacks->state)
     c->log_callbacks->state(c->log_v, c->statebits);
   stash_command(c, 0/*queuejump*/, log_opcallback, 0/*completed*/, v,
-                "log", (char *)0);
+                -1, 0, "log", (char *)0);
   disorder_eclient_polled(c, 0);
   return 0;
 }
@@ -1612,6 +1810,27 @@ static void logentry_rights_changed(disorder_eclient *c,
   }
 }
 
+static void logentry_playlist_created(disorder_eclient *c,
+                                      int attribute((unused)) nvec,
+                                      char **vec) {
+  if(c->log_callbacks->playlist_created)
+    c->log_callbacks->playlist_created(c->log_v, vec[0], vec[1]);
+}
+
+static void logentry_playlist_deleted(disorder_eclient *c,
+                                      int attribute((unused)) nvec,
+                                      char **vec) {
+  if(c->log_callbacks->playlist_deleted)
+    c->log_callbacks->playlist_deleted(c->log_v, vec[0]);
+}
+
+static void logentry_playlist_modified(disorder_eclient *c,
+                                      int attribute((unused)) nvec,
+                                      char **vec) {
+  if(c->log_callbacks->playlist_modified)
+    c->log_callbacks->playlist_modified(c->log_v, vec[0], vec[1]);
+}
+
 static const struct {
   unsigned long bit;
   const char *enable;
index ce5c582..b100106 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2006, 2007 Richard Kettlewell
+ * Copyright (C) 2006-2008 Richard Kettlewell
  *
  * 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
@@ -168,6 +168,15 @@ typedef struct disorder_eclient_log_callbacks {
 
   /** @brief Called when a track is adopted */
   void (*adopted)(void *v, const char *id, const char *who);
+
+  /** @brief Called when a new playlist is created */
+  void (*playlist_created)(void *v, const char *playlist, const char *sharing);
+
+  /** @brief Called when a playlist is modified */
+  void (*playlist_modified)(void *v, const char *playlist, const char *sharing);
+
+  /** @brief Called when a new playlist is deleted */
+  void (*playlist_deleted)(void *v, const char *playlist);
 } disorder_eclient_log_callbacks;
 
 /* State bits */
@@ -222,7 +231,8 @@ typedef void disorder_eclient_no_response(void *v,
  *
  * @p error will be NULL on success.  In this case @p value will be the result
  * (which might be NULL for disorder_eclient_get(),
- * disorder_eclient_get_global() and disorder_eclient_userinfo()).
+ * disorder_eclient_get_global(), disorder_eclient_userinfo() and
+ * disorder_eclient_playlist_get_share()).
  *
  * @p error will be non-NULL on failure.  In this case @p value is always NULL.
  */
@@ -281,7 +291,8 @@ typedef void disorder_eclient_queue_response(void *v,
  * @param vec Pointer to response list
  *
  * @p error will be NULL on success.  In this case @p nvec and @p vec will give
- * the result.
+ * the result, or be -1 and NULL respectively e.g. from
+ * disorder_eclient_playlist_get() if there is no such playlist.
  *
  * @p error will be non-NULL on failure.  In this case @p nvec and @p vec will
  * be 0 and NULL.
@@ -314,6 +325,14 @@ int disorder_eclient_play(disorder_eclient *c,
                           void *v);
 /* add a track to the queue */
 
+int disorder_eclient_playafter(disorder_eclient *c,
+                               const char *target,
+                               int ntracks,
+                               const char **tracks,
+                               disorder_eclient_no_response *completed,
+                               void *v);
+/* insert multiple tracks to an arbitrary point in the queue */
+
 int disorder_eclient_pause(disorder_eclient *c,
                            disorder_eclient_no_response *completed,
                            void *v);
@@ -490,6 +509,40 @@ int disorder_eclient_adopt(disorder_eclient *c,
                            disorder_eclient_no_response *completed,
                            const char *id,
                            void *v);  
+int disorder_eclient_playlists(disorder_eclient *c,
+                               disorder_eclient_list_response *completed,
+                               void *v);
+int disorder_eclient_playlist_delete(disorder_eclient *c,
+                                     disorder_eclient_no_response *completed,
+                                     const char *playlist,
+                                     void *v);
+int disorder_eclient_playlist_lock(disorder_eclient *c,
+                                   disorder_eclient_no_response *completed,
+                                   const char *playlist,
+                                   void *v);
+int disorder_eclient_playlist_unlock(disorder_eclient *c,
+                                     disorder_eclient_no_response *completed,
+                                     void *v);
+int disorder_eclient_playlist_set_share(disorder_eclient *c,
+                                        disorder_eclient_no_response *completed,
+                                        const char *playlist,
+                                        const char *sharing,
+                                        void *v);
+int disorder_eclient_playlist_get_share(disorder_eclient *c,
+                                        disorder_eclient_string_response *completed,
+                                        const char *playlist,
+                                        void *v);
+int disorder_eclient_playlist_set(disorder_eclient *c,
+                                  disorder_eclient_no_response *completed,
+                                  const char *playlist,
+                                  char **tracks,
+                                  int ntracks,
+                                  void *v);
+int disorder_eclient_playlist_get(disorder_eclient *c,
+                                  disorder_eclient_list_response *completed,
+                                  const char *playlist,
+                                  void *v);
+
 #endif
 
 /*
index ee25024..64f1bdb 100644 (file)
@@ -243,7 +243,7 @@ int ev_run(ev_source *ev) {
     } while(n < 0 && errno == EINTR);
     xsigprocmask(SIG_BLOCK, &ev->sigmask, 0);
     if(n < 0) {
-      error(errno, "error calling select");
+      disorder_error(errno, "error calling select");
       if(errno == EBADF) {
        /* If there's a bad FD in the mix then check them all and log what we
         * find, to ease debugging */
@@ -253,13 +253,13 @@ int ev_run(ev_source *ev) {
 
            if(FD_ISSET(fd, &ev->mode[mode].enabled)
               && fstat(fd, &sb) < 0)
-             error(errno, "mode %s fstat %d (%s)",
-                   modenames[mode], fd, ev->mode[mode].fds[n].what);
+             disorder_error(errno, "mode %s fstat %d (%s)",
+                            modenames[mode], fd, ev->mode[mode].fds[n].what);
          }
          for(n = 0; n <= maxfd; ++n)
            if(FD_ISSET(n, &ev->mode[mode].enabled)
               && fstat(n, &sb) < 0)
-             error(errno, "mode %s fstat %d", modenames[mode], n);
+             disorder_error(errno, "mode %s fstat %d", modenames[mode], n);
        }
       }
       return -1;
@@ -539,7 +539,7 @@ static int signal_read(ev_source *ev,
       return ret;
   assert(n != 0);
   if(n < 0 && (errno != EINTR && errno != EAGAIN)) {
-    error(errno, "error reading from signal pipe %d", ev->sigpipe[0]);
+    disorder_error(errno, "error reading from signal pipe %d", ev->sigpipe[0]);
     return -1;
   }
   return 0;
@@ -680,8 +680,8 @@ static int sigchld_callback(ev_source *ev,
         * want the disorder server to bomb out because of it.  So we just log
         * the problem and ignore it.
         */
-       error(errno, "error calling wait4 for PID %lu (broken ptrace?)",
-             (unsigned long)ev->children[n].pid);
+       disorder_error(errno, "error calling wait4 for PID %lu (broken ptrace?)",
+                      (unsigned long)ev->children[n].pid);
        if(errno != ECHILD)
          return -1;
       }
@@ -754,6 +754,36 @@ int ev_child_cancel(ev_source *ev,
   return 0;
 }
 
+/** @brief Terminate and wait for all child processes
+ * @param ev Event loop
+ *
+ * Does *not* call the completion callbacks.  Only used during teardown.
+ */
+void ev_child_killall(ev_source *ev) {
+  int n, rc, w;
+
+  for(n = 0; n < ev->nchildren; ++n) {
+    if(kill(ev->children[n].pid, SIGTERM) < 0) {
+      disorder_error(errno, "sending SIGTERM to pid %lu",
+                    (unsigned long)ev->children[n].pid);
+      ev->children[n].pid = -1;
+    }
+  }
+  for(n = 0; n < ev->nchildren; ++n) {
+    if(ev->children[n].pid == -1)
+      continue;
+    do {
+      rc = waitpid(ev->children[n].pid, &w, 0);
+    } while(rc < 0 && errno == EINTR);
+    if(rc < 0) {
+      disorder_error(errno, "waiting for pid %lu",
+                    (unsigned long)ev->children[n].pid);
+      continue;
+    }
+  }
+  ev->nchildren = 0;
+}
+
 /* socket listeners ***********************************************************/
 
 /** @brief State for a socket listener */
@@ -789,22 +819,22 @@ static int listen_callback(ev_source *ev, int fd, void *u) {
     break;
 #ifdef ECONNABORTED
   case ECONNABORTED:
-    error(errno, "error calling accept");
+    disorder_error(errno, "error calling accept");
     break;
 #endif
 #ifdef EPROTO
   case EPROTO:
     /* XXX on some systems EPROTO should be fatal, but we don't know if
      * we're running on one of them */
-    error(errno, "error calling accept");
+    disorder_error(errno, "error calling accept");
     break;
 #endif
   default:
-    fatal(errno, "error calling accept");
+    disorder_fatal(errno, "error calling accept");
     break;
   }
   if(errno != EINTR && errno != EAGAIN)
-    error(errno, "error calling accept");
+    disorder_error(errno, "error calling accept");
   return 0;
 }
 
@@ -988,8 +1018,8 @@ static int writer_timebound_exceeded(ev_source *ev,
 
   if(!w->abandoned) {
     w->abandoned = 1;
-    error(0, "abandoning writer '%s' because no writes within %ds",
-         w->what, w->timebound);
+    disorder_error(0, "abandoning writer '%s' because no writes within %ds",
+                  w->what, w->timebound);
     w->error = ETIMEDOUT;
   }
   return writer_shutdown(ev, now, u);
@@ -1061,15 +1091,15 @@ static int ev_writer_write(struct sink *sk, const void *s, int n) {
   if(!n)
     return 0;                          /* avoid silliness */
   if(w->fd == -1)
-    error(0, "ev_writer_write on %s after shutdown", w->what);
+    disorder_error(0, "ev_writer_write on %s after shutdown", w->what);
   if(w->spacebound && w->b.end - w->b.start + n > w->spacebound) {
     /* The new buffer contents will exceed the space bound.  We assume that the
      * remote client has gone away and TCP hasn't noticed yet, or that it's got
      * hopelessly stuck. */
     if(!w->abandoned) {
       w->abandoned = 1;
-      error(0, "abandoning writer '%s' because buffer has reached %td bytes",
-           w->what, w->b.end - w->b.start);
+      disorder_error(0, "abandoning writer '%s' because buffer has reached %td bytes",
+                    w->what, w->b.end - w->b.start);
       ev_fd_disable(w->ev, ev_write, w->fd);
       w->error = EPIPE;
       return ev_timeout(w->ev, 0, 0, writer_shutdown, w);
@@ -1178,7 +1208,7 @@ int ev_writer_space_bound(ev_writer *w,
  */
 struct sink *ev_writer_sink(ev_writer *w) {
   if(!w)
-    fatal(0, "ev_write_sink called with null writer");
+    disorder_fatal(0, "ev_write_sink called with null writer");
   return &w->s;
 }
 
index 2838c41..113666c 100644 (file)
@@ -139,6 +139,8 @@ int ev_child_cancel(ev_source *ev,
                    pid_t pid);
 /* cancel a child callback. */
 
+void ev_child_killall(ev_source *ev);
+
 /* socket listeners ***********************************************************/
 
 typedef int ev_listen_callback(ev_source *ev,
index b4de830..ac0c0d8 100644 (file)
 #include "printf.h"
 #include "sink.h"
 
+/** @brief vfprintf() workalike that always accepts UTF-8
+ * @param fp Stream to write to
+ * @param fmt Format string
+ * @param ap Format arguments
+ * @return -1 on error or bytes written on success
+ */
 int byte_vfprintf(FILE *fp, const char *fmt, va_list ap) {
   return byte_vsinkprintf(sink_stdio(0, fp), fmt, ap);
 }
 
+/** @brief fprintf() workalike that always accepts UTF-8
+ * @param fp Stream to write to
+ * @param fmt Format string
+ * @param ... Format arguments
+ * @return -1 on error or bytes written on success
+ */
 int byte_fprintf(FILE *fp, const char *fmt, ...) {
   int n;
   va_list ap;
index d507d0a..573b50a 100644 (file)
@@ -39,6 +39,10 @@ struct hash {
   size_t valuesize;                     /* size of a value */
 };
 
+/** @brief Hash function
+ * @param key Key to hash
+ * @return Hash code
+ */
 static size_t hashfn(const char *key) {
   size_t i = 0;
 
@@ -47,6 +51,9 @@ static size_t hashfn(const char *key) {
   return i;
 }
 
+/** @brief Expand a hash table
+ * @param h Hash table to expand
+ */
 static void grow(hash *h) {
   size_t n, newnslots;
   struct entry **newslots, *e, *f;
@@ -66,6 +73,10 @@ static void grow(hash *h) {
   h->nslots = newnslots;
 }
 
+/** @brief Create a new hash table
+ * @param valuesize Size of value type
+ * @return Hash table
+ */
 hash *hash_new(size_t valuesize) {
   hash *h = xmalloc(sizeof *h);
 
@@ -75,6 +86,18 @@ hash *hash_new(size_t valuesize) {
   return h;
 }
 
+/** @brief Add an element to a hash table
+ * @param h Hash table
+ * @param key Key
+ * @param value New value (will be shallow-copied)
+ * @param mode Add mode
+ * @return 0 on success, -1 if the value could not be added
+ *
+ * Possible add modes are:
+ * - @ref HASH_INSERT - key must not exist yet
+ * - @ref HASH_REPLACE - key must already exist
+ * - @ref HASH_INSERT_OR_REPLACE - key may or may not exist
+ */
 int hash_add(hash *h, const char *key, const void *value, int mode) {
   size_t n = hashfn(key);
   struct entry *e;
@@ -104,6 +127,11 @@ int hash_add(hash *h, const char *key, const void *value, int mode) {
   }
 }
 
+/** @brief Remove an element from a hash table
+ * @param h Hash table
+ * @param key Key to remove
+ * @return 0 on success, -1 if the key wasn't found
+ */
 int hash_remove(hash *h, const char *key) {
   size_t n = hashfn(key);
   struct entry *e, **ee;
@@ -119,6 +147,13 @@ int hash_remove(hash *h, const char *key) {
     return -1;
 }
 
+/** @brief Find an item in a hash table
+ * @param h Hash table
+ * @param key Key to find
+ * @return Pointer to value or NULL if not found
+ *
+ * The return value points inside the hash table and should not be modified.
+ */
 void *hash_find(hash *h, const char *key) {
   size_t n = hashfn(key);
   struct entry *e;
@@ -129,6 +164,21 @@ void *hash_find(hash *h, const char *key) {
   return 0;
 }
 
+/** @brief Visit every item in a hash table
+ * @param h Hash Table
+ * @param callback Function to call for each item
+ * @param u Passed to @p callback
+ * @return 0 on completion, else last return from @p callback
+ *
+ * @p callback should return 0 to continue or non-0 to stop.  The @p key and @p
+ * value pointers passed to it point into the hash table and should not be
+ * modified.
+ *
+ * It's safe to remove items from inside the callback including the visited
+ * one.  It is not safe to add items from inside the callback.
+ *
+ * No particular ordering is used.
+ */
 int hash_foreach(hash *h,
                  int (*callback)(const char *key, void *value, void *u),
                  void *u) {
@@ -145,10 +195,22 @@ int hash_foreach(hash *h,
   return 0;
 }
 
+/** @brief Count the size of a hash table
+ * @param h Hash table
+ * @return Number of elements in hash table
+ */
 size_t hash_count(hash *h) {
   return h->nitems;
 }
 
+/** @brief Get all the keys of a hash table
+ * @param h Hash table
+ * @return NULL-terminated list of keys
+ *
+ * The keys point into the hash table itself and should not be modified.
+ *
+ * No particular ordering is used.
+ */
 char **hash_keys(hash *h) {
   size_t n;
   char **vec = xcalloc(h->nitems + 1, sizeof (char *)), **vp = vec;
index e932f4b..f0efa54 100644 (file)
 #ifndef HASH_H
 #define HASH_H
 
+/** @brief Hash structure
+ *
+ * A hash table has string keys and byte blocks of fixed size as values.
+ */
 typedef struct hash hash;
 struct kvp;
 
index 4ae9d67..efd14b1 100644 (file)
--- a/lib/hex.c
+++ b/lib/hex.c
@@ -75,7 +75,8 @@ int unhexdigitq(int c) {
 int unhexdigit(int c) {
   int d;
 
-  if((d = unhexdigitq(c)) < 0) error(0, "invalid hex digit");
+  if((d = unhexdigitq(c)) < 0)
+    disorder_error(0, "invalid hex digit");
   return d;
 }
 
@@ -96,7 +97,7 @@ uint8_t *unhex(const char *s, size_t *np) {
   int d1, d2;
 
   if((l = strlen(s)) & 1) {
-    error(0, "hex string has odd length");
+    disorder_error(0, "hex string has odd length");
     return 0;
   }
   p = buf = xmalloc_noptr(l / 2);
index 292b3ad..b7318e1 100644 (file)
@@ -40,9 +40,9 @@ const char *local_hostname(void) {
     struct hostent *he;
 
     if(uname(&u) < 0)
-      fatal(errno, "error calling uname");
+      disorder_fatal(errno, "error calling uname");
     if(!(he = gethostbyname(u.nodename)))
-      fatal(0, "cannot resolve '%s'", u.nodename);
+      disorder_fatal(0, "cannot resolve '%s'", u.nodename);
     hostname = xstrdup(he->h_name);
   }
   return hostname;
index 46326ad..370546d 100644 (file)
@@ -44,7 +44,7 @@ int sockaddr_equal(const struct sockaddr *a, const struct sockaddr *b) {
                   &((const struct sockaddr_in6 *)b)->sin6_addr,
                   sizeof (struct in6_addr));
   default:
-    fatal(0, "unknown address family %d", a->sa_family);
+    disorder_fatal(0, "unknown address family %d", a->sa_family);
   }
 }
 
index 5894546..b9a7df8 100644 (file)
@@ -60,11 +60,11 @@ int inputline(const char *tag, FILE *fp, char **lp, int newline) {
     }
   }
   if(ferror(fp)) {
-    error(errno, "error reading %s", tag);
+    disorder_error(errno, "error reading %s", tag);
     return -1;
   } else if(feof(fp)) {
     if(d.nvec != 0)
-      error(0, "error reading %s: unexpected EOF", tag);
+      disorder_error(0, "error reading %s: unexpected EOF", tag);
     return -1;
   }
   dynstr_terminate(&d);
index 5a116de..45aa03c 100644 (file)
--- a/lib/kvp.c
+++ b/lib/kvp.c
@@ -63,6 +63,11 @@ int urldecode(struct sink *sink, const char *ptr, size_t n) {
   return 0;
 }
 
+/** @brief URL-decode a string
+ * @param ptr Start of URL-encoded string
+ * @param n Length of @p ptr
+ * @return Decoded string (0-terminated)
+ */
 static char *decode(const char *ptr, size_t n) {
   struct dynstr d;
   struct sink *s;
diff --git a/lib/log-impl.h b/lib/log-impl.h
deleted file mode 100644 (file)
index de47cf2..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * This file is part of DisOrder.
- * Copyright (C) 2004, 2005, 2007, 2008 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file lib/log-impl.h @brief Errors and logging */
-
-/** @brief Log an error and quit
- *
- * If @c ${DISORDER_FATAL_ABORT} is defined (as anything) then the process
- * is aborted, so you can get a backtrace.
- */
-void disorder_fatal(int errno_value, const char *msg, ...) {
-  va_list ap;
-
-  va_start(ap, msg);
-  elog(LOG_CRIT, errno_value, msg, ap);
-  va_end(ap);
-  if(getenv("DISORDER_FATAL_ABORT")) abort();
-  exitfn(EXIT_FAILURE);
-}
-
-/** @brief Log an error */
-void disorder_error(int errno_value, const char *msg, ...) {
-  va_list ap;
-
-  va_start(ap, msg);
-  elog(LOG_ERR, errno_value, msg, ap);
-  va_end(ap);
-}
-
-/** @brief Log an informational message */
-void disorder_info(const char *msg, ...) {
-  va_list ap;
-
-  va_start(ap, msg);
-  elog(LOG_INFO, 0, msg, ap);
-  va_end(ap);
-}
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-End:
-*/
index f885e88..82ba377 100644 (file)
--- a/lib/log.c
+++ b/lib/log.c
  */
 /** @file lib/log.c @brief Errors and logging
  *
- * All messages are initially emitted by one of the four functions below.
- * debug() is generally invoked via D() so that mostly you just do a test
- * rather than a complete subroutine call.
+ * All messages are initially emitted by one of the four functions
+ * below.  disorder_debug() is generally invoked via D() so that
+ * mostly you just do a test rather than a complete subroutine call.
  *
  * Messages are dispatched via @ref log_default.  This defaults to @ref
  * log_stderr.  daemonize() will turn off @ref log_stderr and use @ref
  * log_syslog instead.
  *
- * fatal() will call exitfn() with a nonzero status.  The default value is
- * exit(), but it should be set to _exit() anywhere but the 'main line' of the
- * program, to guarantee that exit() gets called at most once.
+ * disorder_fatal() will call exitfn() with a nonzero status.  The
+ * default value is exit(), but it should be set to _exit() anywhere
+ * but the 'main line' of the program, to guarantee that exit() gets
+ * called at most once.
  */
 
 #define NO_MEMORY_ALLOCATION
@@ -38,6 +39,7 @@
 #include <errno.h>
 #include <syslog.h>
 #include <sys/time.h>
+#include <time.h>
 
 #include "log.h"
 #include "disorder.h"
@@ -66,6 +68,9 @@ const char *progname;
 /** @brief Filename for debug messages */
 const char *debug_filename;
 
+/** @brief Set to include timestamps in log messages */
+int logdate;
+
 /** @brief Line number for debug messages */
 int debug_lineno;
 
@@ -119,7 +124,15 @@ static void logfp(int pri, const char *msg, void *user) {
    * sanely */
   const char *p;
   
-  if(progname)
+  if(logdate) {
+    char timebuf[64];
+    struct tm *tm;
+    gettimeofday(&tv, 0);
+    tm = localtime(&tv.tv_sec);
+    strftime(timebuf, sizeof timebuf, "%Y-%m-%d %H:%M:%S %Z", tm);
+    fprintf(fp, "%s: ", timebuf);
+  } 
+ if(progname)
     fprintf(fp, "%s: ", progname);
   if(pri <= LOG_ERR)
     fputs("ERROR: ", fp);
@@ -194,15 +207,41 @@ void elog(int pri, int errno_value, const char *fmt, va_list ap) {
   }
 }
 
-#define disorder_fatal fatal
-#define disorder_error error
-#define disorder_info info
+/** @brief Log an error and quit
+ *
+ * If @c ${DISORDER_FATAL_ABORT} is defined (as anything) then the process
+ * is aborted, so you can get a backtrace.
+ */
+void disorder_fatal(int errno_value, const char *msg, ...) {
+  va_list ap;
+
+  va_start(ap, msg);
+  elog(LOG_CRIT, errno_value, msg, ap);
+  va_end(ap);
+  if(getenv("DISORDER_FATAL_ABORT")) abort();
+  exitfn(EXIT_FAILURE);
+}
+
+/** @brief Log an error */
+void disorder_error(int errno_value, const char *msg, ...) {
+  va_list ap;
+
+  va_start(ap, msg);
+  elog(LOG_ERR, errno_value, msg, ap);
+  va_end(ap);
+}
 
-/* shared implementation of vararg functions */
-#include "log-impl.h"
+/** @brief Log an informational message */
+void disorder_info(const char *msg, ...) {
+  va_list ap;
+
+  va_start(ap, msg);
+  elog(LOG_INFO, 0, msg, ap);
+  va_end(ap);
+}
 
 /** @brief Log a debug message */
-void debug(const char *msg, ...) {
+void disorder_debug(const char *msg, ...) {
   va_list ap;
 
   va_start(ap, msg);
index e7f6e43..f1f55d2 100644 (file)
--- a/lib/log.h
+++ b/lib/log.h
@@ -30,13 +30,13 @@ void set_progname(char **argv);
 
 void elog(int pri, int errno_value, const char *fmt, va_list ap);
 
-void fatal(int errno_value, const char *msg, ...) attribute((noreturn))
+void disorder_fatal(int errno_value, const char *msg, ...) attribute((noreturn))
   attribute((format (printf, 2, 3)));
-void error(int errno_value, const char *msg, ...)
+void disorder_error(int errno_value, const char *msg, ...)
   attribute((format (printf, 2, 3)));
-void info(const char *msg, ...)
+void disorder_info(const char *msg, ...)
   attribute((format (printf, 1, 2)));
-void debug(const char *msg, ...)
+void disorder_debug(const char *msg, ...)
   attribute((format (printf, 1, 2)));
 /* report a message of the given class.  @errno_value@ if present an
  * non-zero is included.  @fatal@ terminates the process. */
@@ -55,12 +55,18 @@ extern struct log_output log_stderr, log_syslog, *log_default;
 
 extern const char *debug_filename;
 extern int debug_lineno;
+extern int logdate;
 
+/** @brief Issue a debug message if debugging is turned on
+ * @param x Parenthesized debug arguments
+ *
+ * Use in the format: D(("format string", arg, arg, ...));
+ */
 #define D(x) do {                              \
   if(debugging) {                              \
     debug_filename=__FILE__;                   \
     debug_lineno=__LINE__;                     \
-    debug x;                                   \
+    disorder_debug x;                          \
   }                                            \
 } while(0)
 
index a6f14c3..7175108 100644 (file)
@@ -29,7 +29,7 @@
 #include "event.h"
 #include "log.h"
 
-/* called when bytes are available and at eof */
+/** @brief Called when a log FD is readable */
 static int logfd_readable(ev_source attribute((unused)) *ev,
                          ev_reader *reader,
                          void *ptr,
@@ -43,24 +43,24 @@ static int logfd_readable(ev_source attribute((unused)) *ev,
   while((nl = memchr(ptr, '\n', bytes))) {
     len = nl - (char *)ptr;
     ev_reader_consume(reader, len + 1);
-    info("%s: %.*s", tag, len, (char *)ptr);
+    disorder_info("%s: %.*s", tag, len, (char *)ptr);
     ptr = nl + 1;
     bytes -= len + 1;
   }
   if(eof && bytes) {
-    info("%s: %.*s", tag, (int)bytes, (char *)ptr);
+    disorder_info("%s: %.*s", tag, (int)bytes, (char *)ptr);
     ev_reader_consume(reader, bytes);
   }
   return 0;
 }
 
-/* called when a read error occurs */
+/** @brief Called when a log FD errors */
 static int logfd_error(ev_source attribute((unused)) *ev,
                       int errno_value,
                       void *u) {
   const char *tag = u;
   
-  error(errno_value, "error reading log pipe from %s", tag);
+  disorder_error(errno_value, "error reading log pipe from %s", tag);
   return 0;
 }
 
@@ -74,7 +74,7 @@ static int logfd_error(ev_source attribute((unused)) *ev,
  * it in the parent).
  *
  * Any lines written to this fd (i.e. by the subprocess) will be logged via
- * info(), with @p tag included.
+ * disorder_info(), with @p tag included.
  */
 int logfd(ev_source *ev, const char *tag) {
   int p[2];
@@ -84,7 +84,7 @@ int logfd(ev_source *ev, const char *tag) {
   nonblock(p[0]);
   if(!ev_reader_new(ev, p[0], logfd_readable, logfd_error, (void *)tag,
                     "logfd"))
-    fatal(errno, "error calling ev_reader_new");
+    disorder_fatal(errno, "error calling ev_reader_new");
   return p[1];
 }
 
index 3d49c25..0c80232 100644 (file)
@@ -79,7 +79,7 @@ char *mx_find(const char *name, int report) {
   if(name[0] == '/') {
     if(access(name, O_RDONLY) < 0) {
       if(report)
-        error(errno, "cannot read %s", name);
+        disorder_error(errno, "cannot read %s", name);
       return 0;
     }
     path = xstrdup(name);
@@ -92,7 +92,7 @@ char *mx_find(const char *name, int report) {
     }
     if(n >= include_path.nvec) {
       if(report)
-        error(0, "cannot find '%s' in search path", name);
+        disorder_error(0, "cannot find '%s' in search path", name);
       return 0;
     }
   }
@@ -137,11 +137,11 @@ static int exp_include(int attribute((unused)) nargs,
   /* Read the raw file.  As with mx_expand_file() we insist that the file is a
    * regular file. */
   if((fd = open(path, O_RDONLY)) < 0)
-    fatal(errno, "error opening %s", path);
+    disorder_fatal(errno, "error opening %s", path);
   if(fstat(fd, &sb) < 0)
-    fatal(errno, "error statting %s", path);
+    disorder_fatal(errno, "error statting %s", path);
   if(!S_ISREG(sb.st_mode))
-    fatal(0, "%s: not a regular file", path);
+    disorder_fatal(0, "%s: not a regular file", path);
   while((n = read(fd, buffer, sizeof buffer)) > 0) {
     if(sink_write(output, buffer, n) < 0) {
       xclose(fd);
@@ -149,14 +149,14 @@ static int exp_include(int attribute((unused)) nargs,
     }
   }
   if(n < 0)
-    fatal(errno, "error reading %s", path);
+    disorder_fatal(errno, "error reading %s", path);
   xclose(fd);
   return 0;
 }
 
 /*$ @include{COMMAND}
  *
- * Executes COMMAND via the shell (using "sh -c") and copies its
+ * Executes COMMAND via the shell (using "sh \-c") and copies its
  * standard output to the template output.  The shell command output
  * is not expanded or modified in any other way.
  *
@@ -180,7 +180,7 @@ static int exp_shell(int attribute((unused)) nargs,
     xdup2(p[1], 1);
     xclose(p[1]);
     execlp("sh", "sh", "-c", args[0], (char *)0);
-    fatal(errno, "error executing sh");
+    disorder_fatal(errno, "error executing sh");
   }
   xclose(p[1]);
   while((n = read(p[0], buffer, sizeof buffer))) {
@@ -188,7 +188,7 @@ static int exp_shell(int attribute((unused)) nargs,
       if(errno == EINTR)
        continue;
       else
-       fatal(errno, "error reading from pipe");
+       disorder_fatal(errno, "error reading from pipe");
     }
     if(output->write(output, buffer, n) < 0)
       return -1;
@@ -197,9 +197,9 @@ static int exp_shell(int attribute((unused)) nargs,
   while((n = waitpid(pid, &w, 0)) < 0 && errno == EINTR)
     ;
   if(n < 0)
-    fatal(errno, "error calling waitpid");
+    disorder_fatal(errno, "error calling waitpid");
   if(w)
-    error(0, "shell command '%s' %s", args[0], wstat(w));
+    disorder_error(0, "shell command '%s' %s", args[0], wstat(w));
   return 0;
 }
 
index d194ef5..e11c264 100644 (file)
@@ -197,11 +197,11 @@ const struct mx_node *mx_parse(const char *filename,
      * and the name. */
     dynstr_init(d);
     if(input == end)
-      fatal(0, "%s:%d: invalid expansion syntax (truncated)",
-            filename, e->line);
+      disorder_fatal(0, "%s:%d: invalid expansion syntax (truncated)",
+                     filename, e->line);
     if(!isalnum((unsigned char)*input))
-      fatal(0, "%s:%d: invalid expansion syntax (unexpected %#x)",
-            filename, e->line, (unsigned char)*input);
+      disorder_fatal(0, "%s:%d: invalid expansion syntax (unexpected %#x)",
+                     filename, e->line, (unsigned char)*input);
     while(input < end && (isalnum((unsigned char)*input) || *input == '-'))
       dynstr_append(d, *input++);
     dynstr_terminate(d);
@@ -240,9 +240,9 @@ const struct mx_node *mx_parse(const char *filename,
         }
         if(input >= end) {
           /* We ran out of input without encountering a balanced cbracket */
-         fatal(0, "%s:%d: unterminated expansion argument '%.*s'",
-               filename, argument_start_line,
-               (int)(input - argument_start), argument_start);
+         disorder_fatal(0, "%s:%d: unterminated expansion argument '%.*s'",
+                         filename, argument_start_line,
+                         (int)(input - argument_start), argument_start);
         }
         /* Consistency check */
         assert(*input == cbracket);
@@ -390,8 +390,8 @@ int mx_register_macro(const char *name,
     /* This locates the error to the definition, which may be a line or two
      * beyond the @define command itself.  The backtrace generated by
      * mx_expand() may help more. */
-    error(0, "%s:%d: duplicate definition of '%s'",
-          definition->filename, definition->line, name);
+    disorder_error(0, "%s:%d: duplicate definition of '%s'",
+                   definition->filename, definition->line, name);
 #endif
     return -2;
   }
@@ -432,17 +432,17 @@ int mx_expand(const struct mx_node *m,
   case MX_EXPANSION:
     rc = 0;
     if(!(e = hash_find(expansions, m->name))) {
-      error(0, "%s:%d: unknown expansion name '%s'",
-            m->filename, m->line, m->name);
+      disorder_error(0, "%s:%d: unknown expansion name '%s'",
+                     m->filename, m->line, m->name);
       if(sink_printf(output, "[['%s' unknown]]", m->name) < 0)
         return -1;
     } else if(m->nargs < e->min) {
-      error(0, "%s:%d: expansion '%s' requires %d args, only %d given",
-            m->filename, m->line, m->name, e->min, m->nargs);
+      disorder_error(0, "%s:%d: expansion '%s' requires %d args, only %d given",
+                     m->filename, m->line, m->name, e->min, m->nargs);
       if(sink_printf(output, "[['%s' too few args]]", m->name) < 0)
         return -1;
     } else if(m->nargs > e->max) {
-      error(0, "%s:%d: expansion '%s' takes at most %d args, but %d given",
+      disorder_error(0, "%s:%d: expansion '%s' takes at most %d args, but %d given",
             m->filename, m->line, m->name, e->max, m->nargs);
       if(sink_printf(output, "[['%s' too many args]]", m->name) < 0)
         return -1;
@@ -488,8 +488,8 @@ int mx_expand(const struct mx_node *m,
     if(rc) {
       /* For non-IO errors we generate some backtrace */
       if(rc != -1)
-        error(0,  "  ...in @%s at %s:%d",
-              m->name, m->filename, m->line);
+        disorder_error(0,  "  ...in @%s at %s:%d",
+                       m->name, m->filename, m->line);
       return rc;
     }
     break;
@@ -523,7 +523,7 @@ int mx_expandstr(const struct mx_node *m,
   } else
     *sp = 0;
   if(rc && rc != -1 && what)
-    error(0, "  ...in %s at %s:%d", what, m->filename, m->line);
+    disorder_error(0, "  ...in %s at %s:%d", what, m->filename, m->line);
   return rc;
 }
 
@@ -545,11 +545,11 @@ int mx_expand_file(const char *path,
   const struct mx_node *m;
 
   if((fd = open(path, O_RDONLY)) < 0)
-    fatal(errno, "error opening %s", path);
+    disorder_fatal(errno, "error opening %s", path);
   if(fstat(fd, &sb) < 0)
-    fatal(errno, "error statting %s", path);
+    disorder_fatal(errno, "error statting %s", path);
   if(!S_ISREG(sb.st_mode))
-    fatal(0, "%s: not a regular file", path);
+    disorder_fatal(0, "%s: not a regular file", path);
   sofar = 0;
   b = xmalloc_noptr(sb.st_size);
   while(sofar < sb.st_size) {
@@ -557,16 +557,16 @@ int mx_expand_file(const char *path,
     if(n > 0)
       sofar += n;
     else if(n == 0)
-      fatal(0, "unexpected EOF reading %s", path);
+      disorder_fatal(0, "unexpected EOF reading %s", path);
     else if(errno != EINTR)
-      fatal(errno, "error reading %s", path);
+      disorder_fatal(errno, "error reading %s", path);
   }
   xclose(fd);
   m = mx_parse(path, 1, b, b + sb.st_size);
   rc = mx_expand(m, output, u);
   if(rc && rc != -1)
     /* Mention inclusion in backtrace */
-    error(0, "  ...in inclusion of file '%s'", path);
+    disorder_error(0, "  ...in inclusion of file '%s'", path);
   return rc;
 }
 
index 5f08b90..7f14fd9 100644 (file)
--- a/lib/mem.c
+++ b/lib/mem.c
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004, 2005, 2006, 2007 Richard Kettlewell
+ * Copyright (C) 2004, 2005, 2006, 2007, 2009 Richard Kettlewell
  *
  * 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
@@ -88,7 +88,7 @@ void *xmalloc(size_t n) {
   void *ptr;
 
   if(!(ptr = do_malloc(n)) && n)
-    fatal(errno, "error allocating memory");
+    disorder_fatal(errno, "error allocating memory");
   return ptr;
 }
 
@@ -102,7 +102,7 @@ void *xmalloc(size_t n) {
  */
 void *xrealloc(void *ptr, size_t n) {
   if(!(ptr = do_realloc(ptr, n)) && n)
-    fatal(errno, "error allocating memory");
+    disorder_fatal(errno, "error allocating memory");
   return ptr;
 }
 
@@ -116,7 +116,7 @@ void *xrealloc(void *ptr, size_t n) {
  */
 void *xcalloc(size_t count, size_t size) {
   if(count > SIZE_MAX / size)
-    fatal(0, "excessively large calloc");
+    disorder_fatal(0, "excessively large calloc");
   return xmalloc(count * size);
 }
 
@@ -132,10 +132,24 @@ void *xmalloc_noptr(size_t n) {
   void *ptr;
 
   if(!(ptr = do_malloc_atomic(n)) && n)
-    fatal(errno, "error allocating memory");
+    disorder_fatal(errno, "error allocating memory");
   return ptr;
 }
 
+/** @brief Allocate memory
+ * @param count Number of objects to allocate
+ * @param size Size of one object
+ * @return Pointer to allocated memory
+ *
+ * Terminates the process on error.  IMPORTANT: the allocated memory is NOT
+ * 0-filled (unlike @c calloc()).
+ */
+void *xcalloc_noptr(size_t count, size_t size) {
+  if(count > SIZE_MAX / size)
+    disorder_fatal(0, "excessively large calloc");
+  return xmalloc_noptr(count * size);
+}
+
 /** @brief Reallocate memory
  * @param ptr Block to reallocated
  * @param n Bytes to allocate
@@ -149,7 +163,7 @@ void *xrealloc_noptr(void *ptr, size_t n) {
   if(ptr == 0)
     return xmalloc_noptr(n);
   if(!(ptr = do_realloc(ptr, n)) && n)
-    fatal(errno, "error allocating memory");
+    disorder_fatal(errno, "error allocating memory");
   return ptr;
 }
 
@@ -163,7 +177,7 @@ char *xstrdup(const char *s) {
   char *t;
 
   if(!(t = do_malloc_atomic(strlen(s) + 1)))
-    fatal(errno, "error allocating memory");
+    disorder_fatal(errno, "error allocating memory");
   return strcpy(t, s);
 }
 
@@ -179,7 +193,7 @@ char *xstrndup(const char *s, size_t n) {
   char *t;
 
   if(!(t = do_malloc_atomic(n + 1)))
-    fatal(errno, "error allocating memory");
+    disorder_fatal(errno, "error allocating memory");
   memcpy(t, s, n);
   t[n] = 0;
   return t;
@@ -196,5 +210,7 @@ void xfree(void *ptr) {
 Local Variables:
 c-basic-offset:2
 comment-column:40
+fill-column:79
+indent-tabs-mode:nil
 End:
 */
index 2aa70aa..8a8f245 100644 (file)
--- a/lib/mem.h
+++ b/lib/mem.h
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004-2008 Richard Kettlewell
+ * Copyright (C) 2004-2009 Richard Kettlewell
  *
  * 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
@@ -35,17 +35,18 @@ void *xmalloc(size_t);
 void *xrealloc(void *, size_t);
 void *xcalloc(size_t count, size_t size);
 /* As malloc/realloc/calloc, but
- * 1) succeed or call fatal
+ * 1) succeed or call disorder_fatal
  * 2) always clear (the unused part of) the new allocation
  * 3) are garbage-collected
  */
 
 void *xmalloc_noptr(size_t);
 void *xrealloc_noptr(void *, size_t);
+void *xcalloc_noptr(size_t count, size_t size);
 char *xstrdup(const char *);
 char *xstrndup(const char *, size_t);
 /* As malloc/realloc/strdup, but
- * 1) succeed or call fatal
+ * 1) succeed or call disorder_fatal
  * 2) are garbage-collected
  * 3) allocated space must not contain any pointers
  *
index 6bff61d..9cc54d6 100644 (file)
@@ -328,7 +328,7 @@ const char *mime_parse(const char *s,
       return mime_qp(s);
     if(!strcmp(cte, "7bit") || !strcmp(cte, "8bit"))
       return s;
-    error(0, "unknown content-transfer-encoding '%s'", cte);
+    disorder_error(0, "unknown content-transfer-encoding '%s'", cte);
     return 0;
   }
   return s;
@@ -376,7 +376,7 @@ int mime_multipart(const char *s,
 
   /* We must start with a boundary string */
   if(!isboundary(s, boundary, bl)) {
-    error(0, "mime_multipart: first line is not the boundary string");
+    disorder_error(0, "mime_multipart: first line is not the boundary string");
     return -1;
   }
   /* Keep going until we hit a final boundary */
@@ -385,7 +385,7 @@ int mime_multipart(const char *s,
     start = s;
     while(!isboundary(s, boundary, bl)) {
       if(!(e = strstr(s, "\r\n"))) {
-       error(0, "mime_multipart: line does not end CRLF");
+       disorder_error(0, "mime_multipart: line does not end CRLF");
        return -1;
       }
       s = e + 2;
@@ -566,17 +566,17 @@ int parse_cookie(const char *s,
       continue;
     }
     if(!(s = parsetoken(s, &n, cookie_separator))) {
-      error(0, "parse_cookie: cannot parse attribute name");
+      disorder_error(0, "parse_cookie: cannot parse attribute name");
       return -1;
-    }      
+    }
     s = skipwhite(s, 0);
     if(*s++ != '=') {
-      error(0, "parse_cookie: did not find expected '='");
+      disorder_error(0, "parse_cookie: did not find expected '='");
       return -1;
     }
     s = skipwhite(s, 0);
     if(!(s = mime_parse_word(s, &v, cookie_value_separator))) {
-      error(0, "parse_cookie: cannot parse value for '%s'", n);
+      disorder_error(0, "parse_cookie: cannot parse value for '%s'", n);
       return -1;
     }
     if(n[0] == '$') {
@@ -587,14 +587,14 @@ int parse_cookie(const char *s,
        if(cd->ncookies > 0 && cd->cookies[cd->ncookies-1].path == 0)
          cd->cookies[cd->ncookies-1].path = v;
        else {
-         error(0, "redundant $Path in Cookie: header");
+         disorder_error(0, "redundant $Path in Cookie: header");
          return -1;
        }
       } else if(!strcmp(n, "$Domain")) {
        if(cd->ncookies > 0 && cd->cookies[cd->ncookies-1].domain == 0)
          cd->cookies[cd->ncookies-1].domain = v;
        else {
-         error(0, "redundant $Domain in Cookie: header");
+         disorder_error(0, "redundant $Domain in Cookie: header");
          return -1;
        }
       }
@@ -610,7 +610,7 @@ int parse_cookie(const char *s,
     }
     s = skipwhite(s, 0);
     if(*s && (*s != ',' && *s != ';')) {
-      error(0, "missing separator in Cookie: header");
+      disorder_error(0, "missing separator in Cookie: header");
       return -1;
     }
   }
diff --git a/lib/mixer-alsa.c b/lib/mixer-alsa.c
deleted file mode 100644 (file)
index 1ab85ab..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * This file is part of DisOrder
- * Copyright (C) 2007, 2008 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file lib/mixer-alsa.c
- * @brief ALSA mixer support
- *
- * The documentation for ALSA's mixer support is completely hopeless,
- * which is a particular nuisnace given it's got an incredibly verbose
- * API.  Much of this code is cribbed from
- * alsa-utils-1.0.13/amixer/amixer.c.
- *
- * Mono output devices are supported, but the support is not tested
- * (as I don't one).
- */
-
-#include "common.h"
-
-#if HAVE_ALSA_ASOUNDLIB_H
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stddef.h>
-#include <ctype.h>
-#include <sys/ioctl.h>
-#include <alsa/asoundlib.h>
-
-#include "configuration.h"
-#include "mixer.h"
-#include "log.h"
-#include "syscalls.h"
-
-/** @brief Shared state for ALSA mixer support */
-struct alsa_mixer_state {
-  /** @brief Mixer handle */
-  snd_mixer_t *handle;
-
-  /** @brief Mixer control */
-  snd_mixer_elem_t *elem;
-
-  /** @brief Left channel */
-  snd_mixer_selem_channel_id_t left;
-
-  /** @brief Right channel */
-  snd_mixer_selem_channel_id_t right;
-
-  /** @brief Minimum level */
-  long min;
-
-  /** @brief Maximum level */
-  long max;
-};
-
-/** @brief Destroy a @ref alsa_mixer_state */
-static void alsa_close(struct alsa_mixer_state *h) {
-  /* TODO h->elem */
-  if(h->handle)
-    snd_mixer_close(h->handle);
-}
-
-/** @brief Initialize a @ref alsa_mixer_state */
-static int alsa_open(struct alsa_mixer_state *h) {
-  int err;
-  snd_mixer_selem_id_t *id;
-
-  snd_mixer_selem_id_alloca(&id);
-  memset(h, 0, sizeof h);
-  if((err = snd_mixer_open(&h->handle, 0))) {
-    error(0, "snd_mixer_open: %s", snd_strerror(err));
-    return -1;
-  }
-  if((err = snd_mixer_attach(h->handle, config->device))) {
-    error(0, "snd_mixer_attach %s: %s",
-         config->device, snd_strerror(err));
-    goto error;
-  }
-  if((err = snd_mixer_selem_register(h->handle, 0/*options*/, 0/*classp*/))) {
-    error(0, "snd_mixer_selem_register %s: %s",
-         config->device, snd_strerror(err));
-    goto error;
-  }
-  if((err = snd_mixer_load(h->handle))) {
-    error(0, "snd_mixer_load %s: %s",
-         config->device, snd_strerror(err));
-    goto error;
-  }
-  snd_mixer_selem_id_set_name(id, config->channel);
-  snd_mixer_selem_id_set_index(id, atoi(config->mixer));
-  if(!(h->elem = snd_mixer_find_selem(h->handle, id))) {
-    error(0, "device '%s' mixer control '%s,%s' does not exist",
-         config->device, config->channel, config->mixer);
-    goto error;
-  }
-  if(!snd_mixer_selem_has_playback_volume(h->elem)) {
-    error(0, "device '%s' mixer control '%s,%s' has no playback volume",
-         config->device, config->channel, config->mixer);
-    goto error;
-  }
-  if(snd_mixer_selem_is_playback_mono(h->elem)) {
-    h->left = h->right = SND_MIXER_SCHN_MONO;
-  } else {
-    h->left = SND_MIXER_SCHN_FRONT_LEFT;
-    h->right = SND_MIXER_SCHN_FRONT_RIGHT;
-  }
-  if(!snd_mixer_selem_has_playback_channel(h->elem, h->left)
-     || !snd_mixer_selem_has_playback_channel(h->elem, h->right)) {
-    error(0, "device '%s' mixer control '%s,%s' lacks required playback channels",
-         config->device, config->channel, config->mixer);
-    goto error;
-  }
-  snd_mixer_selem_get_playback_volume_range(h->elem, &h->min, &h->max);
-  return 0;
-error:
-  alsa_close(h);
-  return -1;
-}
-
-/** @brief Convert a level to a percentage */
-static int to_percent(const struct alsa_mixer_state *h, long n) {
-  return (n - h->min) * 100 / (h->max - h->min);
-}
-
-/** @brief Get ALSA volume */
-static int alsa_get(int *left, int *right) {
-  struct alsa_mixer_state h[1];
-  long l, r;
-  int err;
-  
-  if(alsa_open(h))
-    return -1;
-  if((err = snd_mixer_selem_get_playback_volume(h->elem, h->left, &l))
-     || (err = snd_mixer_selem_get_playback_volume(h->elem, h->right, &r))) {
-    error(0, "snd_mixer_selem_get_playback_volume: %s", snd_strerror(err));
-    goto error;
-  }
-  *left = to_percent(h, l);
-  *right = to_percent(h, r);
-  alsa_close(h);
-  return 0;
-error:
-  alsa_close(h);
-  return -1;
-}
-
-/** @brief Convert a percentage to a level */
-static int from_percent(const struct alsa_mixer_state *h, int n) {
-  return h->min + n * (h->max - h->min) / 100;
-}
-
-/** @brief Set ALSA volume */
-static int alsa_set(int *left, int *right) {
-  struct alsa_mixer_state h[1];
-  long l, r;
-  int err;
-  
-  if(alsa_open(h))
-    return -1;
-  /* Set the volume */
-  if(h->left == h->right) {
-    /* Mono output - just use the loudest */
-    if((err = snd_mixer_selem_set_playback_volume
-       (h->elem, h->left,
-        from_percent(h, *left > *right ? *left : *right)))) {
-      error(0, "snd_mixer_selem_set_playback_volume: %s", snd_strerror(err));
-      goto error;
-    }
-  } else {
-    /* Stereo output */
-    if((err = snd_mixer_selem_set_playback_volume
-       (h->elem, h->left, from_percent(h, *left)))
-       || (err = snd_mixer_selem_set_playback_volume
-          (h->elem, h->right, from_percent(h, *right)))) {
-      error(0, "snd_mixer_selem_set_playback_volume: %s", snd_strerror(err));
-      goto error;
-    }
-  }
-  /* Read it back to see what we ended up at */
-  if((err = snd_mixer_selem_get_playback_volume(h->elem, h->left, &l))
-     || (err = snd_mixer_selem_get_playback_volume(h->elem, h->right, &r))) {
-    error(0, "snd_mixer_selem_get_playback_volume: %s", snd_strerror(err));
-    goto error;
-  }
-  *left = to_percent(h, l);
-  *right = to_percent(h, r);
-  alsa_close(h);
-  return 0;
-error:
-  alsa_close(h);
-  return -1;
-}
-
-/** @brief ALSA mixer vtable */
-const struct mixer mixer_alsa = {
-  BACKEND_ALSA,
-  alsa_get,
-  alsa_set,
-  "0",
-  "PCM"
-};
-#endif
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-End:
-*/
diff --git a/lib/mixer-oss.c b/lib/mixer-oss.c
deleted file mode 100644 (file)
index a4ee419..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * This file is part of DisOrder
- * Copyright (C) 2004, 2007, 2008 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file lib/mixer-oss.c
- * @brief OSS mixer support
- *
- * Mono output devices aren't explicitly supported (but may work
- * nonetheless).
- */
-
-#include "common.h"
-
-#if HAVE_SYS_SOUNDCARD_H
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stddef.h>
-#include <sys/ioctl.h>
-#include <sys/soundcard.h>
-
-#include "configuration.h"
-#include "mixer.h"
-#include "log.h"
-#include "syscalls.h"
-#include "mem.h"
-
-/* documentation does not match implementation! */
-#ifndef SOUND_MIXER_READ
-# define SOUND_MIXER_READ(x) MIXER_READ(x)
-#endif
-#ifndef SOUND_MIXER_WRITE
-# define SOUND_MIXER_WRITE(x) MIXER_WRITE(x)
-#endif
-
-/** @brief Channel names */
-static const char *channels[] = SOUND_DEVICE_NAMES;
-
-/** @brief Convert channel name to number */
-static int mixer_channel(const char *c) {
-  unsigned n;
-  
-  if(!c[strspn(c, "0123456789")])
-    return atoi(c);
-  else {
-    for(n = 0; n < sizeof channels / sizeof *channels; ++n)
-      if(!strcmp(channels[n], c))
-       return n;
-    return -1;
-  }
-}
-
-/** @brief Open the OSS mixer device and return its fd */
-static int oss_do_open(void) {
-  int fd;
-  
-  if((fd = open(config->mixer, O_RDWR, 0)) < 0) {
-    static char *reported;
-
-    if(!reported || strcmp(reported, config->mixer)) {
-      if(reported)
-       xfree(reported);
-      reported = xstrdup(config->mixer);
-      error(errno, "error opening %s", config->mixer);
-    }
-  }
-  return fd;
-}
-
-/** @brief Get the OSS mixer setting */
-static int oss_do_get(int *left, int *right, int fd, int ch) {
-  int r;
-  
-  if(ioctl(fd, SOUND_MIXER_READ(ch), &r) == -1) {
-    error(errno, "error reading %s channel %s",
-         config->mixer, config->channel);
-    return -1;
-  }
-  *left = r & 0xff;
-  *right = (r >> 8) & 0xff;
-  return 0;
-}
-
-/** @brief Get OSS volume */
-static int oss_get(int *left, int *right) {
-  int ch, fd;
-
-  if(config->mixer
-     && config->channel
-     && (ch = mixer_channel(config->channel)) != -1) {
-    if((fd = oss_do_open()) < 0)
-      return -1;
-    if(oss_do_get(left, right, fd, ch) < 0) {
-      xclose(fd);
-      return -1;
-    }
-    xclose(fd);
-    return 0;
-  } else
-    return -1;
-}
-
-/** @brief Set OSS volume */
-static int oss_set(int *left, int *right) {
-  int ch, fd, r;
-
-  if(config->mixer
-     && config->channel
-     && (ch = mixer_channel(config->channel)) != -1) {
-    if((fd = oss_do_open()) < 0)
-      return -1;
-    r = (*left & 0xff) + (*right & 0xff) * 256;
-    if(ioctl(fd, SOUND_MIXER_WRITE(ch), &r) == -1) {
-      error(errno, "error changing %s channel %s",
-           config->mixer, config->channel);
-      xclose(fd);
-      return -1;
-    }
-    if(oss_do_get(left, right, fd, ch) < 0) {
-      xclose(fd);
-      return -1;
-    }
-    xclose(fd);
-    return 0;
-  } else
-    return -1;
-}
-
-/** @brief OSS mixer vtable */
-const struct mixer mixer_oss = {
-  BACKEND_OSS,
-  oss_get,
-  oss_set,
-  "/dev/mixer",
-  "pcm"
-};
-#endif
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-End:
-*/
diff --git a/lib/mixer.c b/lib/mixer.c
deleted file mode 100644 (file)
index a24f7eb..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * This file is part of DisOrder
- * Copyright (C) 2007 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file lib/mixer.c
- * @brief Mixer support
- */
-
-#include "common.h"
-
-#include "configuration.h"
-#include "mixer.h"
-#include "log.h"
-#include "mem.h"
-
-/** @brief Whether lack of volume support has been reported yet */
-static int none_reported;
-
-/** @brief Get/set volume stub if volume control is not supported */
-static int none_get_set(int attribute((unused)) *left,
-                       int attribute((unused)) *right) {
-  if(!none_reported) {
-    error(0, "don't know how to get/set volume with this api");
-    none_reported = 1;
-  }
-  return -1;
-}
-
-/** @brief Stub mixer control */
-static const struct mixer mixer_none = {
-  -1,
-  none_get_set,
-  none_get_set,
-  "",
-  ""
-};
-
-/** @brief Table of mixer definitions */
-static const struct mixer *mixers[] = {
-#if HAVE_SYS_SOUNDCARD_H
-  &mixer_oss,
-#endif
-#if HAVE_ALSA_ASOUNDLIB_H
-  &mixer_alsa,
-#endif
-  &mixer_none                          /* make sure array is never empty */
-};
-
-/** @brief Number of mixer definitions */
-#define NMIXERS (sizeof mixers / sizeof *mixers)
-
-/** @brief Find the mixer definition */
-static const struct mixer *find_mixer(int api) {
-  size_t n;
-
-  if(api == -1)
-    api = config->api;
-  for(n = 0; n < NMIXERS; ++n)
-    if(mixers[n]->api == api)
-      return mixers[n];
-  return &mixer_none;
-}
-
-/** @brief Return true if we know how to drive the mixer
- * @param api Sound api or -1 for default
- * @return true if suppored, false otherwise
- */
-int mixer_supported(int api) {
-  const struct mixer *const m = find_mixer(api);
-  return m != &mixer_none;
-}
-
-/** @brief Get/set volume
- * @param api Sound api or -1 for default
- * @param left Left channel level, 0-100
- * @param right Right channel level, 0-100
- * @param set Set volume if non-0
- * @return 0 on success, non-0 on error
- *
- * If getting the volume then @p left and @p right are filled in.
- *
- * If setting the volume then the target levels are read from @p left and
- * @p right, and the actual level set is stored in them.
- */
-int mixer_control(int api, int *left, int *right, int set) {
-  const struct mixer *const m = find_mixer(api);
-
-  /* We impose defaults bizarrely late, but this has the advantage of
-   * not making everything depend on sound libraries */
-  if(!config->mixer)
-    config->mixer = xstrdup(m->device);
-  if(!config->channel)
-    config->channel = xstrdup(m->channel);
-  if(set)
-    return m->set(left, right);
-  else
-    return m->get(left, right);
-}
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-End:
-*/
diff --git a/lib/mixer.h b/lib/mixer.h
deleted file mode 100644 (file)
index 3d42246..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * This file is part of DisOrder
- * Copyright (C) 2004, 2007 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file lib/mixer.h
- * @brief Mixer support
- */
-
-#ifndef MIXER_H
-#define MIXER_H
-
-/** @brief Definition of a mixer */
-struct mixer {
-  /** @brief API used by this mixer */
-  int api;
-
-  /** @brief Get the volume
-   * @param left Where to store left-channel volume
-   * @param right Where to store right-channel volume
-   * @return 0 on success, non-0 on error
-   */
-  int (*get)(int *left, int *right);
-
-  /** @brief Set the volume
-   * @param left Pointer to target left-channel volume
-   * @param right Pointer to target right-channel volume
-   * @return 0 on success, non-0 on error
-   *
-   * @p left and @p right are updated with the actual volume set.
-   */
-  int (*set)(int *left, int *right);
-
-  /** @brief Default device */
-  const char *device;
-
-  /** @brief Default channel */
-  const char *channel;
-};
-
-int mixer_control(int api, int *left, int *right, int set);
-const char *mixer_default_device(int api);
-const char *mixer_default_channel(int api);
-int mixer_supported(int api);
-
-extern const struct mixer mixer_oss;
-extern const struct mixer mixer_alsa;
-
-#endif /* MIXER_H */
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-End:
-*/
index f921791..15cd8ad 100644 (file)
 #include "sink.h"
 #include "vacopy.h"
 
+/** @brief Flags from a converstion specification
+ *
+ * Order significant!
+ */
 enum flags {
   f_thousands = 1,
   f_left = 2,
@@ -43,6 +47,7 @@ enum flags {
   f_precision = 512
 };
 
+/** @brief Possible lengths of a conversion specification */
 enum lengths {
   l_char = 1,
   l_short,
@@ -56,29 +61,65 @@ enum lengths {
 
 struct conversion;
 
+/** @brief Formatter state */
 struct state {
+  /** @brief Output stream */
   struct sink *output;
+
+  /** @brief Number of bytes written */
   int bytes;
+
+  /** @brief Argument list */
   va_list ap;
 };
 
+/** @brief Definition of a conversion specifier */
 struct specifier {
+  /** @brief Defining character ('d', 's' etc) */
   int ch;
+
+  /** @brief Consistency check
+   * @param c Conversion being processed
+   * @return 0 if OK, -1 on error
+   */
   int (*check)(const struct conversion *c);
+
+  /** @brief Generate output
+   * @param s Formatter state
+   * @param c Conversion being processed
+   * @return 0 on success, -1 on error
+   */
   int (*output)(struct state *s, struct conversion *c);
+
+  /** @brief Number base */
   int base;
+
+  /** @brief Digit set */
   const char *digits;
+
+  /** @brief Alternative-form prefix */
   const char *xform;
 };
 
+/** @brief One conversion specified as it's handled */
 struct conversion {
+  /** @brief Flags in this conversion */
   unsigned flags;
+
+  /** @brief Field width (if @ref f_width) */
   int width;
+
+  /** @brief Precision (if @ref f_precision) */
   int precision;
+
+  /** @brief Length modifier or 0 */
   int length;
+
+  /** @brief Specifier used */
   const struct specifier *specifier;
 };
 
+/** @brief Flag characters (order significant!) */
 static const char flags[] = "'-+ #0";
 
 /* write @nbytes@ to the output.  Return -1 on error, 0 on success.
index 333fe7d..69a7986 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004-2008 Richard Kettlewell
+ * Copyright (C) 2004-2009 Richard Kettlewell
  *
  * 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
@@ -55,7 +55,10 @@ const char *const track_origins[] = {
 
 #define VALUE(q, offset, type) *(type *)((char *)q + offset)
 
-/* add new entry @n@ to a doubly linked list just after @b@ */
+/** @brief Insert queue entry @p n just after @p b
+ * @param b Insert after this entry
+ * @param n New entry to insert
+ */
 void queue_insert_entry(struct queue_entry *b, struct queue_entry *n) {
   n->prev = b;
   n->next = b->next;
@@ -86,9 +89,9 @@ static const char *marshall_long(const struct queue_entry *q, size_t offset) {
 
   n = byte_snprintf(buffer, sizeof buffer, "%ld", VALUE(q, offset, long));
   if(n < 0)
-    fatal(errno, "error converting int");
+    disorder_fatal(errno, "error converting int");
   else if((size_t)n >= sizeof buffer)
-    fatal(0, "long converted to decimal is too long");
+    disorder_fatal(0, "long converted to decimal is too long");
   return xstrdup(buffer);
 }
 
@@ -125,9 +128,9 @@ static const char *marshall_time_t(const struct queue_entry *q, size_t offset) {
   n = byte_snprintf(buffer, sizeof buffer,
                    "%"PRIdMAX, (intmax_t)VALUE(q, offset, time_t));
   if(n < 0)
-    fatal(errno, "error converting time");
+    disorder_fatal(errno, "error converting time");
   else if((size_t)n >= sizeof buffer)
-    fatal(0, "time converted to decimal is too long");
+    disorder_fatal(0, "time converted to decimal is too long");
   return xstrdup(buffer);
 }
 
@@ -203,6 +206,7 @@ int queue_unmarshall(struct queue_entry *q, const char *s,
   char **vec;
   int nvec;
 
+  q->pid = -1;                          /* =none */
   if(!(vec = split(s, &nvec, SPLIT_QUOTES, error_handler, u)))
     return -1;
   return queue_unmarshall_vec(q, nvec, vec, error_handler, u);
index 1c70cfd..c287ebe 100644 (file)
@@ -38,9 +38,9 @@ enum playing_state {
    */
   playing_isscratch,
 
-  /** @brief Could not find a player
+  /** @brief OBSOLETE
    *
-   * Obsolete - nothing sets this any more
+   * Formerly meant that no player could be found.  Nothing sets this any more.
    */
   playing_no_player,
 
@@ -188,7 +188,21 @@ struct queue_entry {
   /** @brief How much of track has been played so far (seconds) */
   long sofar;
 
-  /** @brief True if decoder is connected to speaker */
+  /** @brief True if track preparation is underway
+   *
+   * This is set when a decoder has been started and is expected to connect to
+   * the speaker, but the speaker has not sent as @ref SM_ARRIVED message back
+   * yet. */
+  int preparing;
+
+  /** @brief True if decoder is connected to speaker 
+   *
+   * This is not a @ref playing_state for a couple of reasons
+   * - it is orthogonal to @ref playing_started and @ref playing_unplayed
+   * - it would have to be hidden to other users of @c queue_entry
+   *
+   * For non-raw tracks this should always be zero.
+   */
   int prepared;
   /* For DISORDER_PLAYER_PAUSES only: */
 
@@ -203,6 +217,9 @@ struct queue_entry {
 
   /** @brief Owning queue (for Disobedience only) */
   struct queuelike *ql;
+  
+  /** @brief Decoder (or player) process ID */
+  pid_t pid;
 };
 
 void queue_insert_entry(struct queue_entry *b, struct queue_entry *n);
index 3f8f714..e9933a2 100644 (file)
@@ -47,12 +47,12 @@ static void random__rekey(void) {
 
   if(random_fd < 0) {
     if((random_fd = open("/dev/urandom", O_RDONLY)) < 0)
-      fatal(errno, "opening /dev/urandom");
+      disorder_fatal(errno, "opening /dev/urandom");
   }
   if((n = read(random_fd, key, sizeof key)) < 0)
-    fatal(errno, "reading from /dev/urandom");
+    disorder_fatal(errno, "reading from /dev/urandom");
   if((size_t)n < sizeof key)
-    fatal(0, "reading from /dev/urandom: short read");
+    disorder_fatal(0, "reading from /dev/urandom: short read");
   arcfour_setkey(random_ctx, key, sizeof key);
   random_count = 8 * 1024 * 1024;
 }
@@ -75,7 +75,7 @@ void random_get(void *ptr, size_t bytes) {
 
 /** @brief Return a random ID string */
 char *random_id(void) {
-  unsigned long words[2];
+  uint32_t words[2];
   char id[128];
 
   random_get(words, sizeof words);
index cde61cf..1b7cad4 100644 (file)
@@ -143,7 +143,7 @@ const char *regsub(const pcre *re, const char *subject, const char *replace,
       break;
   }
   if(rc <= 0 && rc != PCRE_ERROR_NOMATCH) {
-    error(0, "pcre_exec returned %d, subject '%s'", rc, subject);
+    disorder_error(0, "pcre_exec returned %d, subject '%s'", rc, subject);
     return 0;
   }
   if((flags & REGSUB_MUST_MATCH) && matches == 0)
diff --git a/lib/resample.c b/lib/resample.c
new file mode 100644 (file)
index 0000000..39514ed
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file lib/resample.c
+ * @brief Audio resampling
+ *
+ * General purpose audio format conversion.  Rate conversion only works if the
+ * SRC samplerate library is available, but the bitness/channel/endianness
+ * conversion works regardless.
+ */
+
+#include "common.h"
+#include "resample.h"
+#include "log.h"
+#include "mem.h"
+
+/** @brief Number of intermediate-format samples */
+#define SAMPLES 1024
+
+/** @brief Multiplier for signed formats to allow easy switching */
+#define SIGNED 4
+
+/** @brief Initialize a resampler
+ * @param rs Resampler
+ * @param input_bits Bits/sample in input
+ * @param input_channels Number of input channels
+ * @param input_signed Whether input samples are signed or unsigned
+ * @param input_rate Frames/second in input
+ * @param output_bits Bits/sample in output
+ * @param output_channels Number of output channels
+ * @param output_rate Frames/second in output
+ * @param output_signed Whether output samples are signed or unsigned
+ *
+ * For formats with more than two channels it's assume that the first
+ * two channels are left and right.  No particular meaning is attached
+ * to additional channels other than to assume channel N in an input
+ * means the same as channel N in an output, for N>1.
+ */
+void resample_init(struct resampler *rs,
+                    int input_bits, int input_channels,
+                    int input_rate, int input_signed,
+                    int input_endian,
+                    int output_bits, int output_channels,
+                    int output_rate, int output_signed,
+                    int output_endian) {
+  memset(rs, 0, sizeof *rs);
+  assert(input_bits == 8 || input_bits == 16);
+  assert(output_bits == 8 || output_bits == 16);
+  assert(input_endian == ENDIAN_BIG || input_endian == ENDIAN_LITTLE);
+  assert(output_endian == ENDIAN_BIG || output_endian == ENDIAN_LITTLE);
+  assert(ENDIAN_BIG >= 0 && ENDIAN_BIG < SIGNED);
+  assert(ENDIAN_LITTLE >= 0 && ENDIAN_LITTLE < SIGNED);
+  rs->input_bits = input_bits;
+  rs->input_channels = input_channels;
+  rs->input_rate = input_rate;
+  rs->input_signed = SIGNED * !!input_signed;
+  rs->input_endian = input_endian;
+  rs->output_bits = output_bits;
+  rs->output_channels = output_channels;
+  rs->output_rate = output_rate;
+  rs->output_signed = SIGNED * !!output_signed;
+  rs->output_endian = output_endian;
+  rs->input_bytes_per_sample = (rs->input_bits + 7) / 8;
+  rs->input_bytes_per_frame = rs->input_channels * rs->input_bytes_per_sample;
+  if(rs->input_rate != rs->output_rate) {
+#if HAVE_SAMPLERATE_H
+    int error_;
+    rs->state = src_new(SRC_SINC_BEST_QUALITY, rs->output_channels, &error_);
+    if(!rs->state)
+      disorder_fatal(0, "calling src_new: %s", src_strerror(error_));
+#else
+    disorder_fatal(0, "need to resample audio data but libsamplerate not available");
+#endif
+  }
+}
+
+/** @brief Destroy a resampler
+ * @param rs Resampler
+ */
+void resample_close(struct resampler *rs) {
+#if HAVE_SAMPLERATE_H
+  if(rs->state)
+    src_delete(rs->state);
+#else
+  rs = 0;                               /* quieten compiler */
+#endif
+}
+
+/** @brief Get one sample value and normalize it to [-1,1]
+ * @param rs Resampler state
+ * @param bytes Pointer to input data
+ * @param where Where to store result
+ * @return Number of bytes consumed
+ */
+static size_t resample_get_sample(const struct resampler *rs,
+                                  const uint8_t *bytes,
+                                  float *where) {
+  switch(rs->input_bits + rs->input_signed + rs->input_endian) {
+  case 8+ENDIAN_BIG:
+  case 8+ENDIAN_LITTLE:
+    *where = (bytes[0] - 128)/ 128.0;
+    return 1;
+  case 8+SIGNED+ENDIAN_BIG:
+  case 8+SIGNED+ENDIAN_LITTLE:
+    *where = (int8_t)bytes[0] / 128.0;
+    return 1;
+  case 16+ENDIAN_BIG:
+    *where = (bytes[0] * 256 + bytes[1] - 32768)/ 32768.0;
+    return 2;
+    break;
+  case 16+ENDIAN_LITTLE:
+    *where = (bytes[1] * 256 + bytes[0] - 32768)/ 32768.0;
+    return 2;
+    break;
+  case 16+SIGNED+ENDIAN_BIG:
+    *where = (int16_t)(bytes[0] * 256 + bytes[1])/ 32768.0;
+    return 2;
+    break;
+  case 16+SIGNED+ENDIAN_LITTLE:
+    *where = (int16_t)(bytes[1] * 256 + bytes[0])/ 32768.0;
+    return 2;
+    break;
+  default:
+    assert(!"unsupported sample format");
+  }
+}
+
+static inline int clip(int n, int min, int max) {
+  if(n >= min) {
+    if(n <= max)
+      return n;
+    else
+      return max;
+  } else
+    return min;
+}
+
+/** @brief Store one sample value
+ * @param rs Resampler state
+ * @param sample Sample value
+ * @param bytes Where to store it
+ * @return Number of bytes stored
+ *
+ * The value is clipped naively if it will not fit.
+ */
+static size_t resample_put_sample(const struct resampler *rs,
+                                  float sample,
+                                  uint8_t *bytes) {
+  unsigned value;
+  switch(rs->output_bits + rs->output_signed + rs->output_endian) {
+  case 8+ENDIAN_BIG:
+  case 8+ENDIAN_LITTLE:
+    *bytes = clip(sample * 128.0 + 128, 0, 255);
+    return 1;
+  case 8+SIGNED+ENDIAN_BIG:
+  case 8+SIGNED+ENDIAN_LITTLE:
+    *bytes = clip((int)(sample * 128.0), -128, 127);
+    return 1;
+  case 16+ENDIAN_BIG:                   /* unsigned */
+    value = clip(sample * 32768.0 + 32768, 0, 65535);
+    *bytes++ = value >> 8;
+    *bytes++ = value;
+    return 2;
+  case 16+ENDIAN_LITTLE:
+    value = clip(sample * 32768.0 + 32768, 0, 65535);
+    *bytes++ = value;
+    *bytes++ = value >> 8;
+    return 2;
+  case 16+SIGNED+ENDIAN_BIG:
+    value = clip(sample * 32768.0, -32768, 32767);
+    *bytes++ = value >> 8;
+    *bytes++ = value;
+    return 2;
+  case 16+SIGNED+ENDIAN_LITTLE:
+    value = clip(sample * 32768.0, -32768, 32767);
+    *bytes++ = value;
+    *bytes++ = value >> 8;
+    return 2;
+  default:
+    assert(!"unsupported sample format");
+  }
+}
+
+/** @brief Convert input samples to floats
+ * @param rs Resampler state
+ * @param bytes Input bytes
+ * @param nbytes Number of input bytes
+ * @param floats Where to store converted data
+ *
+ * @p floats must be big enough.  As well as converting to floats this
+ * also converts to the output's channel format.
+ *
+ * Excess input channels are just discarded.  If there are insufficient input
+ * channels the last one is duplicated as often as necessary to make up the
+ * numbers.  This is a rather naff heuristic and may be improved in a future
+ * version, but mostly in DisOrder the output is pretty much always stereo and
+ * the input either mono or stereo, so the result isn't actually going to be
+ * too bad.
+ */
+static void resample_prepare_input(const struct resampler *rs,
+                                   const uint8_t *bytes,
+                                   size_t nframes,
+                                   float *floats) {
+  while(nframes > 0) {
+    int n;
+
+    for(n = 0; n < rs->input_channels && n < rs->output_channels; ++n) {
+      bytes += resample_get_sample(rs, bytes, floats);
+      ++floats;
+    }
+    if(n < rs->input_channels) {
+      /* More input channels; discard them */
+      bytes += (rs->input_channels - n) * rs->input_bytes_per_sample;
+    } else if(n < rs->output_channels) {
+      /* More output channels; duplicate the last input channel */
+      for(; n < rs->output_channels; ++n) {
+        *floats = floats[-1];
+        ++floats;
+      }
+    }
+    --nframes;
+  }
+}
+
+/** @brief Convert between sample formats
+ * @param rs Resampler state
+ * @param bytes Bytes to convert
+ * @param nbytes Number of bytes to convert
+ * @param eof Set an end of input stream
+ * @param converted Called with converted data (possibly more than once)
+ * @param cd Passed to @p cd
+ * @return Number of bytes consumed
+ */
+size_t resample_convert(const struct resampler *rs,
+                        const uint8_t *bytes,
+                        size_t nbytes,
+                        int eof,
+                        void (*converted)(uint8_t *bytes,
+                                          size_t nbytes,
+                                          void *cd),
+                        void *cd) {
+  size_t nframesin = nbytes / (rs->input_bytes_per_frame);
+  size_t nsamplesout;
+  float *input = xcalloc(nframesin * rs->output_channels, sizeof (float));
+  float *output = 0;
+
+  resample_prepare_input(rs, bytes, nframesin, input);
+#if HAVE_SAMPLERATE_H
+  if(rs->state) {
+    /* A sample-rate conversion must be performed */
+    SRC_DATA data;
+    memset(&data, 0, sizeof data);
+    /* Compute how many frames are expected to come out. */
+    size_t maxframesout = nframesin * rs->output_rate / rs->input_rate + 1;
+    output = xcalloc(maxframesout * rs->output_channels, sizeof(float));
+    data.data_in = input;
+    data.data_out = output;
+    data.input_frames = nframesin;
+    data.output_frames = maxframesout;
+    data.end_of_input = eof;
+    data.src_ratio = (double)rs->output_rate / rs->input_rate;
+    D(("nframesin=%zu maxframesout=%zu eof=%d ratio=%d.%06d",
+       nframesin, maxframesout, eof,
+       (int)data.src_ratio,
+       ((int)(data.src_ratio * 1000000) % 1000000)));
+    int error_ = src_process(rs->state, &data);
+    if(error_)
+      disorder_fatal(0, "calling src_process: %s", src_strerror(error_));
+    nframesin = data.input_frames_used;
+    nsamplesout = data.output_frames_gen * rs->output_channels;
+    D(("new nframesin=%zu nsamplesout=%zu", nframesin, nsamplesout));
+  }
+#endif
+  if(!output) {
+    /* No sample-rate conversion required */
+    output = input;
+    nsamplesout = nframesin * rs->output_channels;
+  }
+  const float *op = output;
+  while(nsamplesout > 0) {
+    uint8_t buffer[4096];
+    size_t bufused = 0;
+
+    while(bufused < sizeof buffer && nsamplesout > 0) {
+      bufused += resample_put_sample(rs, *op++, buffer + bufused);
+      --nsamplesout;
+    }
+    converted(buffer, bufused, cd);
+  }
+  if(output != input)
+    xfree(output);
+  xfree(input);
+  eof = 0;             /* quieten compiler */
+  /* Report how many input bytes were actually consumed */
+  //fprintf(stderr, "converted %zu frames\n", nframesin);
+  return nframesin * rs->input_bytes_per_frame;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/resample.h b/lib/resample.h
new file mode 100644 (file)
index 0000000..e99aaeb
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file lib/resample.h
+ * @brief Audio resampling
+ */
+
+#ifndef RESAMPLE_H
+#define RESAMPLE_H
+
+#if HAVE_SAMPLERATE_H
+#include <samplerate.h>
+#endif
+
+#include "byte-order.h"
+
+struct resampler {
+  int input_bits, input_channels, input_rate, input_signed, input_endian;
+  int output_bits, output_channels, output_rate, output_signed, output_endian;
+  int input_bytes_per_sample;
+  int input_bytes_per_frame;
+#if HAVE_SAMPLERATE_H
+  SRC_STATE *state;
+#endif
+};
+
+void resample_init(struct resampler *rs, 
+                   int input_bits, int input_channels, 
+                   int input_rate, int input_signed,
+                   int input_endian,
+                   int output_bits, int output_channels, 
+                   int output_rate, int output_signed,
+                   int output_endian);
+size_t resample_convert(const struct resampler *rs,
+                        const uint8_t *bytes,
+                        size_t nbytes,
+                        int eof,
+                        void (*converted)(uint8_t *bytes,
+                                          size_t nbytes,
+                                          void *cd),
+                        void *cd);
+void resample_close(struct resampler *rs);
+
+#endif /* RESAMPLE_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 05a3342..75a3d8e 100644 (file)
@@ -85,7 +85,7 @@ int parse_rights(const char *s, rights_type *rp, int report) {
   if(!*s) {
     /* You can't have no rights */
     if(report)
-      error(0, "empty rights string");
+      disorder_error(0, "empty rights string");
     return -1;
   }
   while(*s) {
@@ -102,7 +102,7 @@ int parse_rights(const char *s, rights_type *rp, int report) {
          break;
       if(n >= NRIGHTS) {
        if(report)
-          error(0, "unknown user right '%.*s'", (int)l, s);
+          disorder_error(0, "unknown user right '%.*s'", (int)l, s);
        return -1;
       }
       r |= rights_names[n].bit;
index 5b9a82c..524f68d 100644 (file)
@@ -57,20 +57,20 @@ static int getresponse(const char *tag, FILE *in) {
        && line[2] >= '0' && line[2] <= '9') {
       const int rc = 10 * (10 * line[0] + line[1]) + line[2] - 111 * '0';
       if(rc >= 400 && rc <= 599)
-       error(0, "%s: %s", tag, line);
+       disorder_error(0, "%s: %s", tag, line);
       if(line[3] != '-') {
        return rc;
       }
       /* else go round for further response lines */
     } else {
-      error(0, "%s: malformed response: %s", tag, line);
+      disorder_error(0, "%s: malformed response: %s", tag, line);
       return -1;
     }
   }
   if(ferror(in))
-    error(errno, "%s: read error", tag);
+    disorder_error(errno, "%s: read error", tag);
   else
-    error(0, "%s: server closed connection", tag);
+    disorder_error(0, "%s: server closed connection", tag);
   return -1;
 }
 
@@ -93,7 +93,7 @@ static int sendcommand(const char *tag, FILE *out, const char *fmt, ...) {
     rc = fflush(out);
   if(rc >= 0)
     return 0;
-  error(errno, "%s: write error", tag);
+  disorder_error(errno, "%s: write error", tag);
   return -1;
 }
 
@@ -126,7 +126,7 @@ static int sendmailfp(const char *tag, FILE *in, FILE *out,
   time_t now;
   char date[128];
 
-  time(&now);
+  xtime(&now);
   gmtime_r(&now, &ut);
   strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S +0000", &ut);
   gcry_create_nonce(idbuf, sizeof idbuf);
@@ -159,7 +159,7 @@ static int sendmailfp(const char *tag, FILE *in, FILE *out,
      || fprintf(out, "Date: %s\r\n", date) < 0
      || fprintf(out, "\r\n") < 0) {
   write_error:
-    error(errno, "%s: write error", tag);
+    disorder_error(errno, "%s: write error", tag);
     return -1;
   }
   for(ptr = body; *ptr; ++ptr) {
@@ -237,7 +237,7 @@ int sendmail(const char *sender,
       xdup2(outpipe[1], 1);
       execlp(config->sendmail,
              config->sendmail, "-bs", (char *)0);
-      fatal(errno, "executing %s", config->sendmail);
+      disorder_fatal(errno, "executing %s", config->sendmail);
     }
     xclose(inpipe[0]);
     xclose(outpipe[1]);
@@ -253,17 +253,17 @@ int sendmail(const char *sender,
       return -1;
     fdin = xsocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
     if(connect(fdin, ai->ai_addr, ai->ai_addrlen) < 0) {
-      error(errno, "error connecting to %s", tag);
+      disorder_error(errno, "error connecting to %s", tag);
       xclose(fdin);
       return -1;
     }
     if((fdout = dup(fdin)) < 0)
-      fatal(errno, "error calling dup2");
+      disorder_fatal(errno, "error calling dup2");
   }
   if(!(in = fdopen(fdin, "rb")))
-    fatal(errno, "error calling fdopen");
+    disorder_fatal(errno, "error calling fdopen");
   if(!(out = fdopen(fdout, "wb")))
-    fatal(errno, "error calling fdopen");
+    disorder_fatal(errno, "error calling fdopen");
   rc = sendmailfp(tag, in, out, sender, pubsender, recipient, subject,
                  encoding, content_type, body);
   fclose(in);
@@ -274,9 +274,9 @@ int sendmail(const char *sender,
     while(waitpid(pid, &w, 0) < 0 && errno == EINTR)
       ;
     if(w < 0)
-      fatal(errno, "error calling waitpid");
+      disorder_fatal(errno, "error calling waitpid");
     if(w)
-      info("warning: %s -bs: %s", config->sendmail, wstat(w));
+      disorder_info("warning: %s -bs: %s", config->sendmail, wstat(w));
     /* Not fatal - we determine success/failure from the SMTP conversation.
      * Some MTAs exit nonzero if you don't QUIT, which is just stupidly
      * picky. */
@@ -311,7 +311,7 @@ pid_t sendmail_subprocess(const char *sender,
     _exit(0);
   }
   if(pid < 0)
-    error(errno, "error calling fork");
+    disorder_error(errno, "error calling fork");
   return pid;
 }
 
index 9450fea..7a74640 100644 (file)
@@ -77,7 +77,7 @@ static int sink_stdio_write(struct sink *s, const void *buffer, int nbytes) {
   int n = fwrite(buffer, 1, nbytes, S(s)->fp);
   if(n < nbytes) {
     if(S(s)->name)
-      fatal(errno, "error writing to %s", S(s)->name);
+      disorder_fatal(errno, "error writing to %s", S(s)->name);
     else
       return -1;
   }
index 487c495..4928091 100644 (file)
@@ -40,7 +40,7 @@ void speaker_send(int fd, const struct speaker_message *sm) {
     ret = write(fd, sm, sizeof *sm);
   } while(ret < 0 && errno == EINTR);
   if(ret < 0)
-    fatal(errno, "write");
+    disorder_fatal(errno, "write");
 }
 
 /** @brief Receive a speaker message
@@ -55,7 +55,8 @@ int speaker_recv(int fd, struct speaker_message *sm) {
     ret = read(fd, sm, sizeof *sm);
   } while(ret < 0 && errno == EINTR);
   if(ret < 0) {
-    if(errno != EAGAIN) fatal(errno, "recvmsg");
+    if(errno != EAGAIN)
+      disorder_fatal(errno, "recvmsg");
     return -1;
   }
   return ret;
index bd80e7d..24fc970 100644 (file)
@@ -25,6 +25,8 @@
 #ifndef SPEAKER_PROTOCOL_H
 #define SPEAKER_PROTOCOL_H
 
+#include "byte-order.h"
+
 /** @brief A message from the main server to the speaker, or vica versa */
 struct speaker_message {
   /** @brief Message type
@@ -41,6 +43,7 @@ struct speaker_message {
    * - @ref SM_FINISHED
    * - @ref SM_PLAYING
    * - @ref SM_UNKNOWN
+   * - @ref SM_ARRIVED
    */
   int type;
 
@@ -100,6 +103,9 @@ struct speaker_message {
 /** @brief Cancelled track @c id which wasn't playing */
 #define SM_STILLBORN 133
 
+/** @brief A connection for track @c id arrived */
+#define SM_ARRIVED 134
+
 void speaker_send(int fd, const struct speaker_message *sm);
 /* Send a message. */
 
@@ -123,13 +129,6 @@ struct stream_header {
 
   /** @brief Endianness */
   uint8_t endian;
-#define ENDIAN_BIG 1
-#define ENDIAN_LITTLE 2
-#ifdef WORDS_BIGENDIAN
-# define ENDIAN_NATIVE ENDIAN_BIG
-#else
-# define ENDIAN_NATIVE ENDIAN_LITTLE
-#endif
 } attribute((packed));
 
 static inline int formats_equal(const struct stream_header *a,
index 0d5dc18..e13922e 100644 (file)
 
 int mustnotbeminus1(const char *what, int ret) {
   if(ret == -1)
-    fatal(errno, "error calling %s", what);
+    disorder_fatal(errno, "error calling %s", what);
   return ret;
 }
 
 pid_t xfork(void) {
   pid_t pid;
 
-  if((pid = fork()) < 0) fatal(errno, "error calling fork");
+  if((pid = fork()) < 0)
+    disorder_fatal(errno, "error calling fork");
   return pid;
 }
 
 void xclose_guts(const char *path, int line, int fd) {
   if(close(fd) < 0)
-    fatal(errno, "%s:%d: close %d", path, line, fd);
+    disorder_fatal(errno, "%s:%d: close %d", path, line, fd);
 }
 
 void xdup2(int fd1, int fd2) {
@@ -140,7 +141,8 @@ int xnice(int inc) {
    * be -1 */
   errno = 0;
   ret = nice(inc);
-  if(errno) fatal(errno, "error calling nice");
+  if(errno)
+    disorder_fatal(errno, "error calling nice");
   return ret;
 }
 
@@ -148,6 +150,15 @@ void xgettimeofday(struct timeval *tv, struct timezone *tz) {
   mustnotbeminus1("gettimeofday", gettimeofday(tv, tz));
 }
 
+time_t xtime(time_t *whenp) {
+  struct timeval tv;
+
+  xgettimeofday(&tv, NULL);
+  if(whenp)
+    *whenp = tv.tv_sec;
+  return tv.tv_sec;
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index 9f8bd20..e45125a 100644 (file)
@@ -48,6 +48,7 @@ int xprintf(const char *, ...)
 void xfclose(FILE *);
 int xnice(int);
 void xgettimeofday(struct timeval *, struct timezone *);
+time_t xtime(time_t *when);
 /* the above all call @fatal@ if the system call fails */
 
 void nonblock(int fd);
index 930ad24..fe2906c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2007-2009 Richard Kettlewell
  *
  * 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
@@ -22,6 +22,7 @@
 #define TIMEVAL_H
 
 #include <time.h>
+#include <sys/time.h>
 
 static inline struct timeval tvsub(const struct timeval a,
                                    const struct timeval b) {
index 6d5234e..3be725e 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <db.h>
 
+#include "trackdb.h"
 #include "kvp.h"
 
 struct vector;                          /* forward declaration */
@@ -36,6 +37,7 @@ extern DB *trackdb_noticeddb;
 extern DB *trackdb_globaldb;
 extern DB *trackdb_usersdb;
 extern DB *trackdb_scheduledb;
+extern DB *trackdb_playlistsdb;
 
 DBC *trackdb_opencursor(DB *db, DB_TXN *tid);
 /* open a transaction */
diff --git a/lib/trackdb-playlists.c b/lib/trackdb-playlists.c
new file mode 100644 (file)
index 0000000..27d3319
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2008 Richard Kettlewell
+ *
+ * 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 program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+/** @file lib/trackdb-playlists.c
+ * @brief Track database playlist support
+ *
+ * This file implements reading and modification of playlists, including access
+ * control, but not locking or event logging (at least yet).
+ */
+#include "common.h"
+
+#include <errno.h>
+
+#include "trackdb-int.h"
+#include "mem.h"
+#include "log.h"
+#include "configuration.h"
+#include "vector.h"
+#include "eventlog.h"
+#include "validity.h"
+
+static int trackdb_playlist_get_tid(const char *name,
+                                    const char *who,
+                                    char ***tracksp,
+                                    int *ntracksp,
+                                    char **sharep,
+                                    DB_TXN *tid);
+static int trackdb_playlist_set_tid(const char *name,
+                                    const char *who,
+                                    char **tracks,
+                                    int ntracks,
+                                    const char *share,
+                                    DB_TXN *tid);
+static int trackdb_playlist_list_tid(const char *who,
+                                     char ***playlistsp,
+                                     int *nplaylistsp,
+                                     DB_TXN *tid);
+static int trackdb_playlist_delete_tid(const char *name,
+                                       const char *who,
+                                       DB_TXN *tid);
+
+/** @brief Check read access rights
+ * @param name Playlist name
+ * @param who Who wants to read
+ * @param share Playlist share status
+ */
+static int playlist_may_read(const char *name,
+                             const char *who,
+                             const char *share) {
+  char *owner;
+  
+  if(playlist_parse_name(name, &owner, 0))
+    return 0;
+  /* Anyone can read shared playlists */
+  if(!owner)
+    return 1;
+  /* You can always read playlists you own */
+  if(!strcmp(owner, who))
+    return 1;
+  /* You can read public playlists */
+  if(!strcmp(share, "public"))
+    return 1;
+  /* Anything else is prohibited */
+  return 0;
+}
+
+/** @brief Check modify access rights
+ * @param name Playlist name
+ * @param who Who wants to modify
+ * @param share Playlist share status
+ */
+static int playlist_may_write(const char *name,
+                              const char *who,
+                              const char attribute((unused)) *share) {
+  char *owner;
+  
+  if(playlist_parse_name(name, &owner, 0))
+    return 0;
+  /* Anyone can modify shared playlists */
+  if(!owner)
+    return 1;
+  /* You can always modify playlists you own */
+  if(!strcmp(owner, who))
+    return 1;
+  /* Anything else is prohibited */
+  return 0;
+}
+
+/** @brief Get playlist data
+ * @param name Name of playlist
+ * @param who Who wants to know
+ * @param tracksp Where to put list of tracks, or NULL
+ * @param ntracksp Where to put count of tracks, or NULL
+ * @param sharep Where to put sharing type, or NULL
+ * @return 0 on success, non-0 on error
+ *
+ * Possible return values:
+ * - @c 0 on success
+ * - @c ENOENT if the playlist doesn't exist
+ * - @c EINVAL if the playlist name is invalid
+ * - @c EACCES if the playlist cannot be read by @p who
+ */
+int trackdb_playlist_get(const char *name,
+                         const char *who,
+                         char ***tracksp,
+                         int *ntracksp,
+                         char **sharep) {
+  int e;
+
+  if(playlist_parse_name(name, 0, 0)) {
+    disorder_error(0, "invalid playlist name '%s'", name);
+    return EINVAL;
+  }
+  WITH_TRANSACTION(trackdb_playlist_get_tid(name, who,
+                                            tracksp, ntracksp, sharep,
+                                            tid));
+  /* Don't expose libdb error codes too much */
+  if(e == DB_NOTFOUND)
+    e = ENOENT;
+  return e;
+}
+
+static int trackdb_playlist_get_tid(const char *name,
+                                    const char *who,
+                                    char ***tracksp,
+                                    int *ntracksp,
+                                    char **sharep,
+                                    DB_TXN *tid) {
+  struct kvp *k;
+  int e, ntracks;
+  const char *s;
+
+  if((e = trackdb_getdata(trackdb_playlistsdb, name, &k, tid)))
+    return e;
+  /* Get sharability */
+  if(!(s = kvp_get(k, "sharing"))) {
+    disorder_error(0, "playlist '%s' has no 'sharing' key", name);
+    s = "private";
+  }
+  /* Check the read is allowed */
+  if(!playlist_may_read(name, who, s))
+    return EACCES;
+  /* Return sharability */
+  if(sharep)
+    *sharep = xstrdup(s);
+  /* Get track count */
+  if(!(s = kvp_get(k, "count"))) {
+    disorder_error(0, "playlist '%s' has no 'count' key", name);
+    s = "0";
+  }
+  ntracks = atoi(s);
+  if(ntracks < 0) {
+    disorder_error(0, "playlist '%s' has negative count", name);
+    ntracks = 0;
+  }
+  /* Return track count */
+  if(ntracksp)
+    *ntracksp = ntracks;
+  if(tracksp) {
+    /* Get track list */
+    char **tracks = xcalloc(ntracks + 1, sizeof (char *));
+    char b[16];
+
+    for(int n = 0; n < ntracks; ++n) {
+      snprintf(b, sizeof b, "%d", n);
+      if(!(s = kvp_get(k, b))) {
+        disorder_error(0, "playlist '%s' lacks track %d", name, n);
+        s = "unknown";
+      }
+      tracks[n] = xstrdup(s);
+    }
+    tracks[ntracks] = 0;
+    /* Return track list */
+    *tracksp = tracks;
+  }
+  return 0;
+}
+
+/** @brief Modify or create a playlist
+ * @param name Playlist name
+ * @param who User modifying playlist
+ * @param tracks List of tracks to set, or NULL to leave alone
+ * @param ntracks Length of @p tracks
+ * @param share Sharing status, or NULL to leave alone
+ * @return 0 on success, non-0 on error
+ *
+ * If the playlist exists it is just modified.
+ *
+ * If the playlist does not exist it is created.  The default set of tracks is
+ * none, and the default sharing is private (if it is an owned one) or shared
+ * (otherwise).
+ *
+ * If neither @c tracks nor @c share are set then we only do an access check.
+ * The database is never modified (even to create the playlist) in this
+ * situation.
+ *
+ * Possible return values:
+ * - @c 0 on success
+ * - @c EINVAL if the playlist name is invalid
+ * - @c EACCES if the playlist cannot be modified by @p who
+ */
+int trackdb_playlist_set(const char *name,
+                         const char *who,
+                         char **tracks,
+                         int ntracks,
+                         const char *share) {
+  int e;
+  char *owner;
+  
+  if(playlist_parse_name(name, &owner, 0)) {
+    disorder_error(0, "invalid playlist name '%s'", name);
+    return EINVAL;
+  }
+  /* Check valid share types */
+  if(share) {
+    if(owner) {
+      /* Playlists with an owner must be public or private */
+      if(strcmp(share, "public")
+         && strcmp(share, "private")) {
+        disorder_error(0, "playlist '%s' must be public or private", name);
+        return EINVAL;
+      }
+    } else {
+      /* Playlists with no owner must be shared */
+      if(strcmp(share, "shared")) {
+        disorder_error(0, "playlist '%s' must be shared", name);
+        return EINVAL;
+      }
+    }        
+  }
+  /* We've checked as much as we can for now, now go and attempt the change */
+  WITH_TRANSACTION(trackdb_playlist_set_tid(name, who, tracks, ntracks, share,
+                                            tid));
+  return e;
+}
+
+static int trackdb_playlist_set_tid(const char *name,
+                                    const char *who,
+                                    char **tracks,
+                                    int ntracks,
+                                    const char *share,
+                                    DB_TXN *tid) {
+  struct kvp *k;
+  int e;
+  const char *s;
+  const char *event = "playlist_modified";
+
+  if((e = trackdb_getdata(trackdb_playlistsdb, name, &k, tid))
+     && e != DB_NOTFOUND)
+    return e;
+  /* If the playlist doesn't exist set some defaults */
+  if(e == DB_NOTFOUND) {
+    char *defshare, *owner;
+
+    if(playlist_parse_name(name, &owner, &defshare))
+      return EINVAL;
+    /* Can't create a non-shared playlist belonging to someone else.  In fact
+     * this should be picked up by playlist_may_write() below but it's clearer
+     * to do it here. */
+    if(owner && strcmp(owner, who))
+      return EACCES;
+    k = 0;
+    kvp_set(&k, "count", 0);
+    kvp_set(&k, "sharing", defshare);
+    event = "playlist_created";
+  }
+  /* Check that the modification is allowed */
+  if(!(s = kvp_get(k, "sharing"))) {
+    disorder_error(0, "playlist '%s' has no 'sharing' key", name);
+    s = "private";
+  }
+  if(!playlist_may_write(name, who, s))
+    return EACCES;
+  /* If no change was requested then don't even create */
+  if(!share && !tracks)
+    return 0;
+  /* Set the new values */
+  if(share)
+    kvp_set(&k, "sharing", share);
+  if(tracks) {
+    char b[16];
+    int oldcount, n;
+
+    /* Sanity check track count */
+    if(ntracks < 0 || ntracks > config->playlist_max) {
+      disorder_error(0, "invalid track count %d", ntracks);
+      return EINVAL;
+    }
+    /* Set the tracks */
+    for(n = 0; n < ntracks; ++n) {
+      snprintf(b, sizeof b, "%d", n);
+      kvp_set(&k, b, tracks[n]);
+    }
+    /* Get the old track count */
+    if((s = kvp_get(k, "count")))
+      oldcount = atoi(s);
+    else
+      oldcount = 0;
+    /* Delete old slots */
+    for(; n < oldcount; ++n) {
+      snprintf(b, sizeof b, "%d", n);
+      kvp_set(&k, b, NULL);
+    }
+    /* Set the new count */
+    snprintf(b, sizeof b, "%d", ntracks);
+    kvp_set(&k, "count", b);
+  }
+  /* Store the resulting record */
+  e = trackdb_putdata(trackdb_playlistsdb, name, k, tid, 0);
+  /* Log the event */
+  if(!e)
+    eventlog(event, name, kvp_get(k, "sharing"), (char *)0);
+  return e;
+}
+
+/** @brief Get a list of playlists
+ * @param who Who wants to know
+ * @param playlistsp Where to put list of playlists
+ * @param nplaylistsp Where to put count of playlists, or NULL
+ */
+void trackdb_playlist_list(const char *who,
+                           char ***playlistsp,
+                           int *nplaylistsp) {
+  int e;
+
+  WITH_TRANSACTION(trackdb_playlist_list_tid(who, playlistsp, nplaylistsp,
+                                             tid));
+}
+
+static int trackdb_playlist_list_tid(const char *who,
+                                     char ***playlistsp,
+                                     int *nplaylistsp,
+                                     DB_TXN *tid) {
+  struct vector v[1];
+  DBC *c;
+  DBT k[1], d[1];
+  int e;
+
+  vector_init(v);
+  c = trackdb_opencursor(trackdb_playlistsdb, tid);
+  memset(k, 0, sizeof k);
+  while(!(e = c->c_get(c, k, prepare_data(d), DB_NEXT))) {
+    char *name = xstrndup(k->data, k->size), *owner;
+    const char *share = kvp_get(kvp_urldecode(d->data, d->size),
+                                "sharing");
+
+    /* Extract owner; malformed names are skipped */
+    if(playlist_parse_name(name, &owner, 0)) {
+      disorder_error(0, "invalid playlist name '%s' found in database", name);
+      continue;
+    }
+    if(!share) {
+      disorder_error(0, "playlist '%s' has no 'sharing' key", name);
+      continue;
+    }
+    /* Always list public and shared playlists
+     * Only list private ones to their owner
+     * Don't list anything else
+     */
+    if(!strcmp(share, "public")
+       || !strcmp(share, "shared")
+       || (!strcmp(share, "private")
+           && owner && !strcmp(owner, who)))
+      vector_append(v, name);
+  }
+  trackdb_closecursor(c);
+  switch(e) {
+  case DB_NOTFOUND:
+    break;
+  case DB_LOCK_DEADLOCK:
+    return e;
+  default:
+    disorder_fatal(0, "c->c_get: %s", db_strerror(e));
+  }
+  vector_terminate(v);
+  if(playlistsp)
+    *playlistsp = v->vec;
+  if(nplaylistsp)
+    *nplaylistsp = v->nvec;
+  return 0;
+}
+
+/** @brief Delete a playlist
+ * @param name Playlist name
+ * @param who Who is deleting it
+ * @return 0 on success, non-0 on error
+ *
+ * Possible return values:
+ * - @c 0 on success
+ * - @c EINVAL if the playlist name is invalid
+ * - @c EACCES if the playlist cannot be modified by @p who
+ * - @c ENOENT if the playlist doesn't exist
+ */
+int trackdb_playlist_delete(const char *name,
+                            const char *who) {
+  int e;
+  char *owner;
+  
+  if(playlist_parse_name(name, &owner, 0)) {
+    disorder_error(0, "invalid playlist name '%s'", name);
+    return EINVAL;
+  }
+  /* We've checked as much as we can for now, now go and attempt the change */
+  WITH_TRANSACTION(trackdb_playlist_delete_tid(name, who, tid));
+  if(e == DB_NOTFOUND)
+    e = ENOENT;
+  return e;
+}
+
+static int trackdb_playlist_delete_tid(const char *name,
+                                       const char *who,
+                                       DB_TXN *tid) {
+  struct kvp *k;
+  int e;
+  const char *s;
+
+  if((e = trackdb_getdata(trackdb_playlistsdb, name, &k, tid)))
+    return e;
+  /* Check that modification is allowed */
+  if(!(s = kvp_get(k, "sharing"))) {
+    disorder_error(0, "playlist '%s' has no 'sharing' key", name);
+    s = "private";
+  }
+  if(!playlist_may_write(name, who, s))
+    return EACCES;
+  /* Delete the playlist */
+  e = trackdb_delkey(trackdb_playlistsdb, name, tid);
+  if(!e)
+    eventlog("playlist_deleted", name, 0);
+  return e;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 5fd7c32..5f6860e 100644 (file)
@@ -41,6 +41,9 @@ void trackdb_open(int attribute((unused)) flags) {
 void trackdb_init(int attribute((unused)) flags) {
 }
 
+void trackdb_deinit(ev_source attribute((unused)) *ev) {
+}
+
 int trackdb_readable(void) {
   return 0;
 }
index e1bbfc8..628498a 100644 (file)
@@ -59,6 +59,7 @@
 #include "unidata.h"
 #include "base64.h"
 #include "sendmail.h"
+#include "validity.h"
 
 #define RESCAN "disorder-rescan"
 #define DEADLOCK "disorder-deadlock"
@@ -82,8 +83,14 @@ int trackdb_existing_database;
 
 /* setup and teardown ********************************************************/
 
-static const char *home;                /* home had better not change */
-DB_ENV *trackdb_env;                   /* db environment */
+/** @brief Database home directory
+ *
+ * All database files live below here.  It had better never change.
+ */
+static const char *home;
+
+/** @brief Database environment */
+DB_ENV *trackdb_env;
 
 /** @brief The tracks database
  * - Keys are UTF-8(NFC(unicode(path name)))
@@ -157,11 +164,55 @@ DB *trackdb_scheduledb;
  */
 DB *trackdb_usersdb;
 
-static pid_t db_deadlock_pid = -1;      /* deadlock manager PID */
-static pid_t rescan_pid = -1;           /* rescanner PID */
-static int initialized, opened;         /* state */
+/** @brief The playlists database
+ * - Keys are playlist names
+ * - Values are encoded key-value pairs
+ * - Data is user data and cannot be reconstructed
+ */
+DB *trackdb_playlistsdb;
+
+/** @brief Deadlock manager PID */
+static pid_t db_deadlock_pid = -1;
+
+/** @brief Rescanner PID */
+static pid_t rescan_pid = -1;
+
+/** @brief Set when the database environment exists */
+static int initialized;
+
+/** @brief Set when databases are open */
+static int opened;
+
+/** @brief Current stats subprocess PIDs */
+static hash *stats_pids;
+
+/** @brief PID of current random track chooser (disorder-choose) */
+static pid_t choose_pid = -1;
+
+/** @brief Our end of pipe from disorder-choose */
+static int choose_fd;
+
+/** @brief Callback to supply random track to */
+static random_callback *choose_callback;
+
+/** @brief Accumulator for output from disorder-choose */
+static struct dynstr choose_output;
+
+/** @brief Current completion status of disorder-choose
+ * A bitmap of @ref CHOOSE_READING and @ref CHOOSE_RUNNING.
+ */
+static unsigned choose_complete;
+
+/* @brief Exit status from disorder-choose */
+static int choose_status;
+
+/** @brief disorder-choose process is running */
+#define CHOOSE_RUNNING 1
+
+/** @brief disorder-choose pipe is still open */
+#define CHOOSE_READING 2
 
-/* comparison function for keys */
+/** @brief Comparison function for filename-based keys */
 static int compare(DB attribute((unused)) *db_,
                   const DBT *a, const DBT *b) {
   return compare_path_raw(a->data, a->size, b->data, b->size);
@@ -196,7 +247,7 @@ void trackdb_init(int flags) {
   ++initialized;
   if(home) {
     if(strcmp(home, config->home))
-      fatal(0, "cannot change db home without server restart");
+      disorder_fatal(0, "cannot change db home without server restart");
     home = config->home;
   }
 
@@ -214,14 +265,14 @@ void trackdb_init(int flags) {
      * The socket, not being a regular file, is excepted.
      */
     if(!(dp = opendir(config->home)))
-      fatal(errno, "error reading %s", config->home);
+      disorder_fatal(errno, "error reading %s", config->home);
     while((de = readdir(dp))) {
       byte_xasprintf(&p, "%s/%s", config->home, de->d_name);
       if(lstat(p, &st) == 0
          && S_ISREG(st.st_mode)
          && (st.st_mode & 077)) {
         if(chmod(p, st.st_mode & 07700) < 0)
-          fatal(errno, "cannot chmod %s", p);
+          disorder_fatal(errno, "cannot chmod %s", p);
       }
       xfree(p);
     }
@@ -229,15 +280,15 @@ void trackdb_init(int flags) {
   }
 
   /* create environment */
-  if((err = db_env_create(&trackdb_env, 0))) fatal(0, "db_env_create: %s",
-                                                   db_strerror(err));
+  if((err = db_env_create(&trackdb_env, 0)))
+    disorder_fatal(0, "db_env_create: %s", db_strerror(err));
   if((err = trackdb_env->set_alloc(trackdb_env,
                                    xmalloc_noptr, xrealloc_noptr, xfree)))
-    fatal(0, "trackdb_env->set_alloc: %s", db_strerror(err));
+    disorder_fatal(0, "trackdb_env->set_alloc: %s", db_strerror(err));
   if((err = trackdb_env->set_lk_max_locks(trackdb_env, 10000)))
-    fatal(0, "trackdb_env->set_lk_max_locks: %s", db_strerror(err));
+    disorder_fatal(0, "trackdb_env->set_lk_max_locks: %s", db_strerror(err));
   if((err = trackdb_env->set_lk_max_objects(trackdb_env, 10000)))
-    fatal(0, "trackdb_env->set_lk_max_objects: %s", db_strerror(err));
+    disorder_fatal(0, "trackdb_env->set_lk_max_objects: %s", db_strerror(err));
   if((err = trackdb_env->open(trackdb_env, config->home,
                               DB_INIT_LOG
                               |DB_INIT_LOCK
@@ -246,7 +297,8 @@ void trackdb_init(int flags) {
                               |DB_CREATE
                               |recover_type[recover],
                               0600)))
-    fatal(0, "trackdb_env->open %s: %s", config->home, db_strerror(err));
+    disorder_fatal(0, "trackdb_env->open %s: %s",
+                   config->home, db_strerror(err));
   trackdb_env->set_errpfx(trackdb_env, "DB");
   trackdb_env->set_errfile(trackdb_env, stderr);
   trackdb_env->set_verbose(trackdb_env, DB_VERB_DEADLOCK, 1);
@@ -255,7 +307,7 @@ void trackdb_init(int flags) {
   D(("initialized database environment"));
 }
 
-/* called when deadlock manager terminates */
+/** @brief Called when deadlock manager terminates */
 static int reap_db_deadlock(ev_source attribute((unused)) *ev,
                             pid_t attribute((unused)) pid,
                             int status,
@@ -263,13 +315,25 @@ static int reap_db_deadlock(ev_source attribute((unused)) *ev,
                             void attribute((unused)) *u) {
   db_deadlock_pid = -1;
   if(initialized)
-    fatal(0, "deadlock manager unexpectedly terminated: %s",
-          wstat(status));
+    disorder_fatal(0, "deadlock manager unexpectedly terminated: %s",
+                   wstat(status));
   else
     D(("deadlock manager terminated: %s", wstat(status)));
   return 0;
 }
 
+/** @brief Start a subprogram
+ * @param ev Event loop
+ * @param outputfd File descriptor to redirect @c stdout to, or -1
+ * @param prog Program name
+ * @param ... Arguments
+ * @return PID
+ *
+ * Starts a subprocess.  Adds the following arguments:
+ * - @c --config to ensure the right config file is used
+ * - @c --debug or @c --no-debug to match debug settings
+ * - @c --syslog or @c --no-syslog to match log settings
+ */
 static pid_t subprogram(ev_source *ev, int outputfd, const char *prog,
                         ...) {
   pid_t pid;
@@ -299,17 +363,21 @@ static pid_t subprogram(ev_source *ev, int outputfd, const char *prog,
     }
     /* ensure we don't leak privilege anywhere */
     if(setuid(geteuid()) < 0)
-      fatal(errno, "error calling setuid");
+      disorder_fatal(errno, "error calling setuid");
     /* If we were negatively niced, undo it.  We don't bother checking for 
     * error, it's not that important. */
     setpriority(PRIO_PROCESS, 0, 0);
     execvp(prog, (char **)args);
-    fatal(errno, "error invoking %s", prog);
+    disorder_fatal(errno, "error invoking %s", prog);
   }
   return pid;
 }
 
-/* start deadlock manager */
+/** @brief Start deadlock manager
+ * @param ev Event loop
+ *
+ * Called from the main server (only).
+ */
 void trackdb_master(ev_source *ev) {
   assert(db_deadlock_pid == -1);
   db_deadlock_pid = subprogram(ev, -1, DEADLOCK, (char *)0);
@@ -317,8 +385,34 @@ void trackdb_master(ev_source *ev) {
   D(("started deadlock manager"));
 }
 
-/* close environment */
-void trackdb_deinit(void) {
+/** @brief Kill a subprocess and wait for it to terminate
+ * @param ev Event loop or NULL
+ * @param pid Process ID or -1
+ * @param what Description of subprocess
+ *
+ * Used during trackdb_deinit().  This function blocks so don't use it for
+ * normal teardown as that will hang the server.
+ */
+static void terminate_and_wait(ev_source *ev,
+                               pid_t pid,
+                               const char *what) {
+  int err;
+
+  if(pid == -1)
+    return;
+  if(kill(pid, SIGTERM) < 0)
+    disorder_fatal(errno, "error killing %s", what);
+  /* wait for the rescanner to finish */
+  while(waitpid(pid, &err, 0) == -1 && errno == EINTR)
+    ;
+  if(ev)
+    ev_child_cancel(ev, pid);
+}
+
+/** @brief Close database environment
+ * @param ev Event loop
+ */
+void trackdb_deinit(ev_source *ev) {
   int err;
 
   /* sanity checks */
@@ -327,28 +421,36 @@ void trackdb_deinit(void) {
 
   /* close the environment */
   if((err = trackdb_env->close(trackdb_env, 0)))
-    fatal(0, "trackdb_env->close: %s", db_strerror(err));
+    disorder_fatal(0, "trackdb_env->close: %s", db_strerror(err));
 
-  if(rescan_pid != -1) {
-    /* shut down the rescanner */
-    if(kill(rescan_pid, SIGTERM) < 0)
-      fatal(errno, "error killing rescanner");
-    /* wait for the rescanner to finish */
-    while(waitpid(rescan_pid, &err, 0) == -1 && errno == EINTR)
-      ;
-  }
+  terminate_and_wait(ev, rescan_pid, "disorder-rescan");
+  rescan_pid = -1;
+  terminate_and_wait(ev, choose_pid, "disorder-choose");
+  choose_pid = -1;
 
-  /* TODO kill any stats subprocesses */
+  if(stats_pids) {
+    char **ks = hash_keys(stats_pids);
 
-  /* finally terminate the deadlock manager */
-  if(db_deadlock_pid != -1 && kill(db_deadlock_pid, SIGTERM) < 0)
-    fatal(errno, "error killing deadlock manager");
-  db_deadlock_pid = -1;
+    while(*ks) {
+      pid_t pid = atoi(*ks++);
+      terminate_and_wait(ev, pid, "disorder-stats");
+    }
+    stats_pids = NULL;
+  }
 
+  terminate_and_wait(ev, db_deadlock_pid, "disorder-deadlock");
+  db_deadlock_pid = -1;
   D(("deinitialized database environment"));
 }
 
-/* open a specific database */
+/** @brief Open a specific database
+ * @param path Relative path to database
+ * @param dbflags Database flags: DB_DUP, DB_DUPSORT, etc
+ * @param dbtype Database type: DB_HASH, DB_BTREE, etc
+ * @param openflags Open flags: DB_RDONLY, DB_CREATE, etc
+ * @param mode Permission mask: usually 0666
+ * @return Database handle
+ */
 static DB *open_db(const char *path,
                    u_int32_t dbflags,
                    DBTYPE dbtype,
@@ -360,22 +462,22 @@ static DB *open_db(const char *path,
   D(("open %s", path));
   path = config_get_file(path);
   if((err = db_create(&db, trackdb_env, 0)))
-    fatal(0, "db_create %s: %s", path, db_strerror(err));
+    disorder_fatal(0, "db_create %s: %s", path, db_strerror(err));
   if(dbflags)
     if((err = db->set_flags(db, dbflags)))
-      fatal(0, "db->set_flags %s: %s", path, db_strerror(err));
+      disorder_fatal(0, "db->set_flags %s: %s", path, db_strerror(err));
   if(dbtype == DB_BTREE)
     if((err = db->set_bt_compare(db, compare)))
-      fatal(0, "db->set_bt_compare %s: %s", path, db_strerror(err));
+      disorder_fatal(0, "db->set_bt_compare %s: %s", path, db_strerror(err));
   if((err = db->open(db, 0, path, 0, dbtype,
                      openflags | DB_AUTO_COMMIT, mode))) {
     if((openflags & DB_CREATE) || errno != ENOENT) {
       if((err2 = db->close(db, 0)))
-        error(0, "db->close: %s", db_strerror(err2));
+        disorder_error(0, "db->close: %s", db_strerror(err2));
       trackdb_close();
       trackdb_env->close(trackdb_env,0);
       trackdb_env = 0;
-      fatal(0, "db->open %s: %s", path, db_strerror(err));
+      disorder_fatal(0, "db->open %s: %s", path, db_strerror(err));
     }
     db->close(db, 0);
     db = 0;
@@ -411,32 +513,32 @@ void trackdb_open(int flags) {
     s = trackdb_get_global("_dbversion");
     /* Close the database again,  we'll open it property below */
     if((err = trackdb_globaldb->close(trackdb_globaldb, 0)))
-      fatal(0, "error closing global.db: %s", db_strerror(err));
+      disorder_fatal(0, "error closing global.db: %s", db_strerror(err));
     trackdb_globaldb = 0;
     /* Convert version string to an integer */
     oldversion = s ? atol(s) : 1;
     if(oldversion > config->dbversion) {
       /* Database is from the future; we never allow this. */
-      fatal(0, "this version of DisOrder is too old for database version %ld",
-            oldversion);
+      disorder_fatal(0, "this version of DisOrder is too old for database version %ld",
+                     oldversion);
     }
     if(oldversion < config->dbversion) {
       /* Database version is out of date */
       switch(flags & TRACKDB_UPGRADE_MASK) {
       case TRACKDB_NO_UPGRADE:
         /* This database needs upgrading but this is not permitted */
-        fatal(0, "database needs upgrading from %ld to %ld",
-              oldversion, config->dbversion);
+        disorder_fatal(0, "database needs upgrading from %ld to %ld",
+                       oldversion, config->dbversion);
       case TRACKDB_CAN_UPGRADE:
         /* This database needs upgrading */
-        info("invoking disorder-dbupgrade to upgrade from %ld to %ld",
+        disorder_info("invoking disorder-dbupgrade to upgrade from %ld to %ld",
              oldversion, config->dbversion);
         pid = subprogram(0, -1, "disorder-dbupgrade", (char *)0);
         while(waitpid(pid, &err, 0) == -1 && errno == EINTR)
           ;
         if(err)
-          fatal(0, "disorder-dbupgrade %s", wstat(err));
-        info("disorder-dbupgrade succeeded");
+          disorder_fatal(0, "disorder-dbupgrade %s", wstat(err));
+        disorder_info("disorder-dbupgrade succeeded");
         break;
       case TRACKDB_OPEN_FOR_UPGRADE:
         break;
@@ -446,13 +548,13 @@ void trackdb_open(int flags) {
     }
     if(oldversion == config->dbversion && (flags & TRACKDB_OPEN_FOR_UPGRADE)) {
       /* This doesn't make any sense */
-      fatal(0, "database is already at current version");
+      disorder_fatal(0, "database is already at current version");
     }
     trackdb_existing_database = 1;
   } else {
     if(flags & TRACKDB_OPEN_FOR_UPGRADE) {
       /* Cannot upgrade a new database */
-      fatal(0, "cannot upgrade a database that does not exist");
+      disorder_fatal(0, "cannot upgrade a database that does not exist");
     }
     /* This is a brand new database */
     trackdb_existing_database = 0;
@@ -460,7 +562,7 @@ void trackdb_open(int flags) {
   /* open the databases */
   if(!(trackdb_usersdb = open_db("users.db",
                                  0, DB_HASH, dbflags, 0600)))
-    fatal(0, "cannot open users.db");
+    disorder_fatal(0, "cannot open users.db");
   trackdb_tracksdb = open_db("tracks.db",
                              DB_RECNUM, DB_BTREE, dbflags, 0666);
   trackdb_searchdb = open_db("search.db",
@@ -472,7 +574,8 @@ void trackdb_open(int flags) {
   trackdb_noticeddb = open_db("noticed.db",
                              DB_DUPSORT, DB_BTREE, dbflags, 0666);
   trackdb_scheduledb = open_db("schedule.db", 0, DB_HASH, dbflags, 0666);
-  if(!trackdb_existing_database) {
+  trackdb_playlistsdb = open_db("playlists.db", 0, DB_HASH, dbflags, 0666);
+  if(!trackdb_existing_database && !(flags & TRACKDB_READ_ONLY)) {
     /* Stash the database version */
     char buf[32];
 
@@ -483,17 +586,17 @@ void trackdb_open(int flags) {
   D(("opened databases"));
 }
 
-/* close track databases */
+/** @brief Close track databases */
 void trackdb_close(void) {
   int err;
 
   /* sanity checks */
   assert(opened == 1);
   --opened;
-#define CLOSE(N, V) do {                                        \
-  if(V && (err = V->close(V, 0)))                               \
-    fatal(0, "error closing %s: %s", N, db_strerror(err));      \
-  V = 0;                                                        \
+#define CLOSE(N, V) do {                                                \
+  if(V && (err = V->close(V, 0)))                                       \
+    disorder_fatal(0, "error closing %s: %s", N, db_strerror(err));     \
+  V = 0;                                                                \
 } while(0)
   CLOSE("tracks.db", trackdb_tracksdb);
   CLOSE("search.db", trackdb_searchdb);
@@ -503,6 +606,7 @@ void trackdb_close(void) {
   CLOSE("noticed.db", trackdb_noticeddb);
   CLOSE("schedule.db", trackdb_scheduledb);
   CLOSE("users.db", trackdb_usersdb);
+  CLOSE("playlists.db", trackdb_playlistsdb);
   D(("closed databases"));
 }
 
@@ -533,15 +637,21 @@ int trackdb_getdata(DB *db,
       *kp = 0;
     return err;
   case DB_LOCK_DEADLOCK:
-    error(0, "error querying database: %s", db_strerror(err));
+    disorder_error(0, "error querying database: %s", db_strerror(err));
     return err;
   default:
-    fatal(0, "error querying database: %s", db_strerror(err));
+    disorder_fatal(0, "error querying database: %s", db_strerror(err));
   }
 }
 
-/* encode and store a database entry.  Returns 0, DB_KEYEXIST or
- * DB_LOCK_DEADLOCK. */
+/** @brief Encode and store a database entry
+ * @param db Database
+ * @param track Track name
+ * @param k List of key/value pairs to store
+ * @param tid Owning transaction
+ * @param flags DB flags e.g. DB_NOOVERWRITE
+ * @return 0, DB_KEYEXIST or DB_LOCK_DEADLOCK
+ */
 int trackdb_putdata(DB *db,
                     const char *track,
                     const struct kvp *k,
@@ -556,10 +666,10 @@ int trackdb_putdata(DB *db,
   case DB_KEYEXIST:
     return err;
   case DB_LOCK_DEADLOCK:
-    error(0, "error updating database: %s", db_strerror(err));
+    disorder_error(0, "error updating database: %s", db_strerror(err));
     return err;
   default:
-    fatal(0, "error updating database: %s", db_strerror(err));
+    disorder_fatal(0, "error updating database: %s", db_strerror(err));
   }
 }
 
@@ -580,26 +690,33 @@ int trackdb_delkey(DB *db,
   case DB_NOTFOUND:
     return 0;
   case DB_LOCK_DEADLOCK:
-    error(0, "error updating database: %s", db_strerror(err));
+    disorder_error(0, "error updating database: %s", db_strerror(err));
     return err;
   default:
-    fatal(0, "error updating database: %s", db_strerror(err));
+    disorder_fatal(0, "error updating database: %s", db_strerror(err));
   }
 }
 
-/* open a database cursor */
+/** @brief Open a database cursor
+ * @param db Database
+ * @param tid Owning transaction
+ * @return Cursor
+ */
 DBC *trackdb_opencursor(DB *db, DB_TXN *tid) {
   int err;
   DBC *c;
 
   switch(err = db->cursor(db, tid, &c, 0)) {
   case 0: break;
-  default: fatal(0, "error creating cursor: %s", db_strerror(err));
+  default: disorder_fatal(0, "error creating cursor: %s", db_strerror(err));
   }
   return c;
 }
 
-/* close a database cursor; returns 0 or DB_LOCK_DEADLOCK */
+/** @brief Close a database cursor
+ * @param c Cursor
+ * @return 0 or DB_LOCK_DEADLOCK
+ */
 int trackdb_closecursor(DBC *c) {
   int err;
 
@@ -608,14 +725,23 @@ int trackdb_closecursor(DBC *c) {
   case 0:
     return err;
   case DB_LOCK_DEADLOCK:
-    error(0, "error closing cursor: %s", db_strerror(err));
+    disorder_error(0, "error closing cursor: %s", db_strerror(err));
     return err;
   default:
-    fatal(0, "error closing cursor: %s", db_strerror(err));
+    disorder_fatal(0, "error closing cursor: %s", db_strerror(err));
   }
 }
 
-/* delete a (key,data) pair.  Returns 0, DB_NOTFOUND or DB_LOCK_DEADLOCK. */
+/** @brief Delete a key/data pair
+ * @param db Database
+ * @param word Key
+ * @param track Data
+ * @param tid Owning transaction
+ * @return 0, DB_NOTFOUND or DB_LOCK_DEADLOCK
+ *
+ * Used by the search and tags databases, hence the odd parameter names.
+ * See also register_word().
+ */
 int trackdb_delkeydata(DB *db,
                        const char *word,
                        const char *track,
@@ -635,59 +761,81 @@ int trackdb_delkeydata(DB *db,
       err = 0;
       break;
     case DB_LOCK_DEADLOCK:
-      error(0, "error updating database: %s", db_strerror(err));
+      disorder_error(0, "error updating database: %s", db_strerror(err));
       break;
     default:
-      fatal(0, "c->c_del: %s", db_strerror(err));
+      disorder_fatal(0, "c->c_del: %s", db_strerror(err));
     }
     break;
   case DB_NOTFOUND:
     break;
   case DB_LOCK_DEADLOCK:
-    error(0, "error updating database: %s", db_strerror(err));
+    disorder_error(0, "error updating database: %s", db_strerror(err));
     break;
   default:
-    fatal(0, "c->c_get: %s", db_strerror(err));
+    disorder_fatal(0, "c->c_get: %s", db_strerror(err));
   }
   if(trackdb_closecursor(c)) err = DB_LOCK_DEADLOCK;
   return err;
 }
 
-/* start a transaction */
+/** @brief Start a transaction
+ * @return Transaction
+ */
 DB_TXN *trackdb_begin_transaction(void) {
   DB_TXN *tid;
   int err;
 
   if((err = trackdb_env->txn_begin(trackdb_env, 0, &tid, 0)))
-    fatal(0, "trackdb_env->txn_begin: %s", db_strerror(err));
+    disorder_fatal(0, "trackdb_env->txn_begin: %s", db_strerror(err));
   return tid;
 }
 
-/* abort transaction */
+/** @brief Abort transaction
+ * @param tid Transaction (or NULL)
+ *
+ * If @p tid is NULL then nothing happens.
+ */
 void trackdb_abort_transaction(DB_TXN *tid) {
   int err;
 
   if(tid)
     if((err = tid->abort(tid)))
-      fatal(0, "tid->abort: %s", db_strerror(err));
+      disorder_fatal(0, "tid->abort: %s", db_strerror(err));
 }
 
-/* commit transaction */
+/** @brief Commit transaction
+ * @param tid Transaction (must not be NULL)
+ */
 void trackdb_commit_transaction(DB_TXN *tid) {
   int err;
 
   if((err = tid->commit(tid, 0)))
-    fatal(0, "tid->commit: %s", db_strerror(err));
+    disorder_fatal(0, "tid->commit: %s", db_strerror(err));
 }
 
 /* search/tags shared code ***************************************************/
 
-/* comparison function used by dedupe() */
+/** @brief Comparison function used by dedupe()
+ * @param a Pointer to first key
+ * @param b Pointer to second key
+ * @return -1, 0 or 1
+ *
+ * Passed to qsort().
+ */
 static int wordcmp(const void *a, const void *b) {
   return strcmp(*(const char **)a, *(const char **)b);
 }
 
-/* sort and de-dupe VEC */
+/** @brief Sort and de-duplicate @p vec
+ * @param vec Vector to sort
+ * @param nvec Length of @p vec
+ * @return @p vec
+ *
+ * The returned vector is NULL-terminated, and there must be room for this NULL
+ * even if there are no duplicates (i.e. it must have more than @p nvec
+ * elements.)
+ */
 static char **dedupe(char **vec, int nvec) {
   int m, n;
 
@@ -703,7 +851,17 @@ static char **dedupe(char **vec, int nvec) {
   return vec;
 }
 
-/* update a key/track database.  Returns 0 or DB_DEADLOCK. */
+/** @brief Store a key/data pair
+ * @param db Database
+ * @param what Description
+ * @param track Data
+ * @param word Key
+ * @param tid Owning transaction
+ * @return 0 or DB_DEADLOCK
+ *
+ * Used by the search and tags databases, hence the odd parameter names.
+ * See also trackdb_delkeydata().
+ */
 static int register_word(DB *db, const char *what,
                          const char *track, const char *word,
                          DB_TXN *tid) {
@@ -716,22 +874,31 @@ static int register_word(DB *db, const char *what,
   case DB_KEYEXIST:
     return 0;
   case DB_LOCK_DEADLOCK:
-    error(0, "error updating %s.db: %s", what, db_strerror(err));
+    disorder_error(0, "error updating %s.db: %s", what, db_strerror(err));
     return err;
   default:
-    fatal(0, "error updating %s.db: %s", what,  db_strerror(err));
+    disorder_fatal(0, "error updating %s.db: %s", what,  db_strerror(err));
   }
 }
 
 /* search primitives *********************************************************/
 
-/* return true iff NAME is a trackname_display_ pref */
+/** @brief Return true iff @p name is a trackname_display_ pref
+ * @param name Preference name
+ * @return Non-zero iff @p name is a trackname_display_ pref
+ */
 static int is_display_pref(const char *name) {
   static const char prefix[] = "trackname_display_";
   return !strncmp(name, prefix, (sizeof prefix) - 1);
 }
 
-/** @brief Word_Break property tailor that treats underscores as spaces */
+/** @brief Word_Break property tailor that treats underscores as spaces
+ * @param c Code point
+ * @return Tailored property or -1 to use standard value
+ *
+ * Passed to utf32_word_split() when splitting a track name into words.
+ * See word_split() and @ref unicode_property_tailor.
+ */
 static int tailor_underscore_Word_Break_Other(uint32_t c) {
   switch(c) {
   default:
@@ -757,7 +924,20 @@ static size_t remove_combining_chars(uint32_t *s, size_t ns) {
   return t - start;
 }
 
-/** @brief Normalize and split a string using a given tailoring */
+/** @brief Normalize and split a string using a given tailoring
+ * @param v Where to store words from string
+ * @param s Input string
+ * @param pt Word_Break property tailor, or NULL
+ *
+ * The output words will be:
+ * - case-folded
+ * - have any combination characters stripped
+ * - not include any word break code points (as tailored)
+ *
+ * Used by track_to_words(), with @p pt set to @ref
+ * tailor_underscore_Word_Break_Other, and by normalize_tag() with no
+ * tailoring.
+ */
 static void word_split(struct vector *v,
                        const char *s,
                        unicode_property_tailor *pt) {
@@ -813,7 +993,11 @@ static char *normalize_tag(const char *s, size_t ns) {
   return d->vec;
 }
 
-/* compute the words of a track name */
+/** @brief Compute the words of a track name
+ * @param track Track name
+ * @param p Preferences (for display prefs)
+ * @return NULL-terminated, de-duplicated list or words
+ */
 static char **track_to_words(const char *track,
                              const struct kvp *p) {
   struct vector v;
@@ -831,7 +1015,10 @@ static char **track_to_words(const char *track,
   return dedupe(v.vec, v.nvec);
 }
 
-/* return nonzero iff WORD is a stopword */
+/** @brief Test for a stopword
+ * @param word Word
+ * @return Non-zero if @p word is a stopword
+ */
 static int stopword(const char *word) {
   int n;
 
@@ -841,7 +1028,12 @@ static int stopword(const char *word) {
   return n < config->stopword.n;
 }
 
-/* record that WORD appears in TRACK.  Returns 0 or DB_LOCK_DEADLOCK. */
+/** @brief Register a search term
+ * @param track Track name
+ * @param word A word that appears in the name of @p track
+ * @param tid Owning transaction
+ * @return  0 or DB_LOCK_DEADLOCK
+ */
 static int register_search_word(const char *track, const char *word,
                                 DB_TXN *tid) {
   if(stopword(word)) return 0;
@@ -850,7 +1042,13 @@ static int register_search_word(const char *track, const char *word,
 
 /* Tags **********************************************************************/
 
-/* Return nonzero if C is a valid tag character */
+/** @brief Test for tag characters
+ * @param c Character
+ * @return Non-zero if @p c is a tag character
+ *
+ * The current rule is that commas and the control characters 0-31 are not
+ * allowed but anything else is permitted.  This is arguably a bit loose.
+ */
 static int tagchar(int c) {
   switch(c) {
   case ',':
@@ -860,7 +1058,12 @@ static int tagchar(int c) {
   }
 }
 
-/* Parse and de-dupe a tag list.  If S=0 then assumes "". */
+/** @brief Parse a tag list
+ * @param s Tag list or NULL (equivalent to "")
+ * @return Parsed tag list
+ *
+ * The tags will be normalized (as per normalize_tag()) and de-duplicated.
+ */
 char **parsetags(const char *s) {
   const char *t;
   struct vector v;
@@ -889,15 +1092,38 @@ char **parsetags(const char *s) {
   return dedupe(v.vec, v.nvec);
 }
 
-/* Record that TRACK has TAG.  Returns 0 or DB_LOCK_DEADLOCK. */
+/** @brief Register a tag
+ * @param track Track name
+ * @param tag Tag name
+ * @param tid Owning transaction
+ * @return 0 or DB_LOCK_DEADLOCK
+ */
 static int register_tag(const char *track, const char *tag, DB_TXN *tid) {
   return register_word(trackdb_tagsdb, "tags", track, tag, tid);
 }
 
 /* aliases *******************************************************************/
 
-/* compute the alias and store at aliasp.  Returns 0 or DB_LOCK_DEADLOCK.  If
- * there is no alias sets *aliasp to 0. */
+/** @brief Compute an alias
+ * @param aliasp Where to put alias (gets NULL if none)
+ * @param track Track to find alias for
+ * @param p Prefs for @p track
+ * @param tid Owning transaction
+ * @return 0 or DB_LOCK_DEADLOCK
+ *
+ * This function looks up the track name parts for @p track.  By default these
+ * amount to the original values from the track name but are overridden by
+ * preferences.
+ *
+ * These values are then substituted into the pattern defined by the @b alias
+ * command; see disorder_config(5) for the syntax.
+ *
+ * The track is only considered to have an alias if all of the following are
+ * true:
+ * - a preference was used for at least one name part
+ * - the result differs from the original track name
+ * - the result does not match any existing track or alias
+ */
 static int compute_alias(char **aliasp,
                          const char *track,
                          const struct kvp *p,
@@ -960,15 +1186,28 @@ static int compute_alias(char **aliasp,
   }
 }
 
-/* get track and prefs data (if tp/pp not null pointers).  Returns 0 on
- * success, DB_NOTFOUND if the track does not exist or DB_LOCK_DEADLOCK.
- * Always sets the return values, even if only to null pointers. */
+/** @brief Assert that no alias is allowed for gettrackdata() */
+#define GTD_NOALIAS 0x0001
+
+/** @brief Get all track data
+ * @param track Track to look up; aliases allowed unless @ref GTD_NOALIAS
+ * @param tp Where to put track data (if not NULL)
+ * @param pp Where to put preferences (if not NULL)
+ * @param actualp Where to put real (i.e. non-alias) path (if not NULL)
+ * @param flags Flag values, see below
+ * @param tid Owning transaction
+ * @return 0, DB_NOTFOUND (track doesn't exist) or DB_LOCK_DEADLOCK
+ *
+ * Possible flags values are:
+ * - @ref GTD_NOALIAS to assert that an alias is not allowed
+ *
+ * The return values are always set (even if to NULL).
+ */
 static int gettrackdata(const char *track,
                         struct kvp **tp,
                         struct kvp **pp,
                         const char **actualp,
                         unsigned flags,
-#define GTD_NOALIAS 0x0001
                         DB_TXN *tid) {
   int err;
   const char *actual = track;
@@ -977,7 +1216,8 @@ static int gettrackdata(const char *track,
   if((err = trackdb_getdata(trackdb_tracksdb, track, &t, tid))) goto done;
   if((actual = kvp_get(t, "_alias_for"))) {
     if(flags & GTD_NOALIAS) {
-      error(0, "alias passed to gettrackdata where real path required");
+      disorder_error(0,
+                     "alias passed to gettrackdata where real path required");
       abort();
     }
     if((err = trackdb_getdata(trackdb_tracksdb, actual, &t, tid))) goto done;
@@ -998,8 +1238,12 @@ done:
 
 /* trackdb_notice() **********************************************************/
 
-/** @brief notice a possibly new track
+/** @brief Notice a possibly new track
+ * @param track NFC UTF-8 track name
+ * @param path Raw path name (i.e. the bytes that came out of readdir())
  * @return @c DB_NOTFOUND if new, 0 if already known
+ *
+ * @c disorder-rescan is responsible for normalizing the track name.
  */
 int trackdb_notice(const char *track,
                    const char *path) {
@@ -1018,11 +1262,13 @@ int trackdb_notice(const char *track,
   return err;
 }
 
-/** @brief notice a possibly new track
+/** @brief Notice a possibly new track
  * @param track NFC UTF-8 track name
- * @param path Raw path name
- * @param tid Transaction ID
+ * @param path Raw path name (i.e. the bytes that came out of readdir())
+ * @param tid Owning transaction
  * @return @c DB_NOTFOUND if new, 0 if already known, @c DB_LOCK_DEADLOCK also
+ *
+ * @c disorder-rescan is responsible for normalizing the track name.
  */
 int trackdb_notice_tid(const char *track,
                        const char *path,
@@ -1042,7 +1288,7 @@ int trackdb_notice_tid(const char *track,
   /* this is a real track */
   t_changed += kvp_set(&t, "_alias_for", 0);
   t_changed += kvp_set(&t, "_path", path);
-  time(&now);
+  xtime(&now);
   if(ret == DB_NOTFOUND) {
     /* It's a new track; record the time */
     byte_xasprintf(&noticed, "%lld", (long long)now);
@@ -1083,7 +1329,8 @@ int trackdb_notice_tid(const char *track,
                                         make_key(&data, track), 0)) {
     case 0: break;
     case DB_LOCK_DEADLOCK: return err;
-    default: fatal(0, "error updating noticed.db: %s", db_strerror(err));
+    default:
+      disorder_fatal(0, "error updating noticed.db: %s", db_strerror(err));
     }
   }
   return ret;
@@ -1091,7 +1338,14 @@ int trackdb_notice_tid(const char *track,
 
 /* trackdb_obsolete() ********************************************************/
 
-/* obsolete a track */
+/** @brief Obsolete a track
+ * @param track Track name
+ * @param tid Owning transaction
+ * @return 0 or DB_LOCK_DEADLOCK
+ *
+ * Discards a track from the database when it's known not to exist any more.
+ * Returns 0 even if it wasn't recorded.
+ */
 int trackdb_obsolete(const char *track, DB_TXN *tid) {
   int err, n;
   struct kvp *p;
@@ -1175,7 +1429,14 @@ static const struct statinfo {
   B(bt_over_pgfree),
 };
 
-/* look up stats for DB */
+/** @brief Look up DB statistics
+ * @param v Where to store stats
+ * @param database Database
+ * @param si Pointer to table of stats
+ * @param nsi Size of @p si
+ * @param tid Owning transaction
+ * @return 0 or DB_LOCK_DEADLOCK
+ */
 static int get_stats(struct vector *v,
                      DB *database,
                      const struct statinfo *si,
@@ -1191,10 +1452,10 @@ static int get_stats(struct vector *v,
     case 0:
       break;
     case DB_LOCK_DEADLOCK:
-      error(0, "error querying database: %s", db_strerror(err));
+      disorder_error(0, "error querying database: %s", db_strerror(err));
       return err;
     default:
-      fatal(0, "error querying database: %s", db_strerror(err));
+      disorder_fatal(0, "error querying database: %s", db_strerror(err));
     }
     for(n = 0; n < nsi; ++n) {
       byte_xasprintf(&str, "%s=%"PRIuMAX, si[n].name,
@@ -1242,7 +1503,12 @@ static int register_search_entry(struct search_entry *se,
   return nse;
 }
 
-/* find the top COUNT words in the search database */
+/** @brief Find the top @p count words in the search database
+ * @param v Where to format the result
+ * @param count Maximum number of words
+ * @param tid Owning transaction
+ * @return 0 or DB_LOCK_DEADLOCK
+ */
 static int search_league(struct vector *v, int count, DB_TXN *tid) {
   struct search_entry *se;
   DBT k, d;
@@ -1271,10 +1537,10 @@ static int search_league(struct vector *v, int count, DB_TXN *tid) {
     err = 0;
     break;
   case DB_LOCK_DEADLOCK:
-    error(0, "error querying search database: %s", db_strerror(err));
+    disorder_error(0, "error querying search database: %s", db_strerror(err));
     break;
   default:
-    fatal(0, "error querying search database: %s", db_strerror(err));
+    disorder_fatal(0, "error querying search database: %s", db_strerror(err));
   }
   if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
   if(err) return err;
@@ -1291,7 +1557,13 @@ static int search_league(struct vector *v, int count, DB_TXN *tid) {
 #define SI(what) statinfo_##what, \
                  sizeof statinfo_##what / sizeof (struct statinfo)
 
-/* return a list of database stats */
+/** @brief Return a list of database stats
+ * @param nstatsp Where to store number of lines (or NULL)
+ * @return Database stats output
+ *
+ * This is called by @c disorder-stats.  Don't call it directly from elsewhere
+ * as it can take unreasonably long.
+ */
 char **trackdb_stats(int *nstatsp) {
   DB_TXN *tid;
   struct vector v;
@@ -1320,6 +1592,7 @@ fail:
   return v.vec;
 }
 
+/** @brief State structure tracking @c disorder-stats */
 struct stats_details {
   void (*done)(char *data, void *u);
   void *u;
@@ -1329,6 +1602,12 @@ struct stats_details {
   struct dynstr data[1];                /* data read from pipe */
 };
 
+/** @brief Called when @c disorder-stats may have completed
+ * @param d Pointer to state structure
+ *
+ * Called from stats_finished() and stats_read().  Only proceeds when the
+ * process has terminated and the output is complete.
+ */
 static void stats_complete(struct stats_details *d) {
   char *s;
 
@@ -1345,8 +1624,16 @@ static void stats_complete(struct stats_details *d) {
   d->done(d->data->vec, d->u);
 }
 
+/** @brief Called when @c disorder-stats exits
+ * @param ev Event loop
+ * @param pid Process ID
+ * @param status Exit status
+ * @param rusage Resource usage
+ * @param u Pointer to state structure (@ref stats_details)
+ * @return 0
+ */
 static int stats_finished(ev_source attribute((unused)) *ev,
-                          pid_t attribute((unused)) pid,
+                          pid_t pid,
                           int status,
                           const struct rusage attribute((unused)) *rusage,
                           void *u) {
@@ -1354,11 +1641,23 @@ static int stats_finished(ev_source attribute((unused)) *ev,
 
   d->exited = 1;
   if(status)
-    error(0, "disorder-stats %s", wstat(status));
+    disorder_error(0, "disorder-stats %s", wstat(status));
   stats_complete(d);
+  char *k;
+  byte_xasprintf(&k, "%lu", (unsigned long)pid);
+  hash_remove(stats_pids, k);
   return 0;
 }
 
+/** @brief Called when pipe from @c disorder-stats is readable
+ * @param ev Event loop
+ * @param reader Reader state
+ * @param ptr Pointer to bytes read
+ * @param bytes Number of bytes available
+ * @param eof Set at end of file
+ * @param u Pointer to state structure (@ref stats_details)
+ * @return 0
+ */
 static int stats_read(ev_source attribute((unused)) *ev,
                       ev_reader *reader,
                       void *ptr,
@@ -1375,17 +1674,31 @@ static int stats_read(ev_source attribute((unused)) *ev,
   return 0;
 }
 
+/** @brief Called when pipe from @c disorder-stats errors
+ * @param ev Event loop
+ * @param errno_value Error code
+ * @param u Pointer to state structure (@ref stats_details)
+ * @return 0
+ */
 static int stats_error(ev_source attribute((unused)) *ev,
                        int errno_value,
                        void *u) {
   struct stats_details *const d = u;
 
-  error(errno_value, "error reading from pipe to disorder-stats");
+  disorder_error(errno_value, "error reading from pipe to disorder-stats");
   d->closed = 1;
   stats_complete(d);
   return 0;
 }
 
+/** @brief Get database statistics via background process
+ * @param ev Event loop
+ * @param done Called on completion
+ * @param u Passed to @p done
+ *
+ * Within the main server use this instead of trackdb_stats(), which can take
+ * unreasonably long.
+ */
 void trackdb_stats_subprocess(ev_source *ev,
                               void (*done)(char *data, void *u),
                               void *u) {
@@ -1402,7 +1715,13 @@ void trackdb_stats_subprocess(ev_source *ev,
   ev_child(ev, pid, 0, stats_finished, d);
   if(!ev_reader_new(ev, p[0], stats_read, stats_error, d,
                     "disorder-stats reader"))
-    fatal(0, "ev_reader_new for disorder-stats reader failed");
+    disorder_fatal(0, "ev_reader_new for disorder-stats reader failed");
+  /* Remember the PID */
+  if(!stats_pids)
+    stats_pids = hash_new(1);
+  char *k;
+  byte_xasprintf(&k, "%lu", (unsigned long)pid);
+  hash_add(stats_pids, k, "", HASH_INSERT);
 }
 
 /** @brief Parse a track name part preference
@@ -1459,7 +1778,12 @@ static const char *trackdb__default(const char *track, const char *name) {
   return 0;
 }
 
-/* set a pref (remove if value=0) */
+/** @brief Set a preference
+ * @param track Track to modify
+ * @param name Preference name
+ * @param value New value, or NULL to erase any existing value
+ * @return 0 on success or non-zero if not allowed to set preference
+ */
 int trackdb_set(const char *track,
                 const char *name,
                 const char *value) {
@@ -1556,13 +1880,20 @@ fail:
   return err == 0 ? 0 : -1;
 }
 
-/* get a pref */
+/** @brief Get the value of a preference
+ * @param track Track name
+ * @param name Preference name
+ * @return Preference value or NULL if it's not set
+ */
 const char *trackdb_get(const char *track,
                         const char *name) {
   return kvp_get(trackdb_get_all(track), name);
 }
 
-/* get all prefs as a 0-terminated array */
+/** @brief Get all preferences for a track
+ * @param track Track name
+ * @return Linked list of preferences
+ */
 struct kvp *trackdb_get_all(const char *track) {
   struct kvp *t, *p, **pp;
   DB_TXN *tid;
@@ -1582,7 +1913,10 @@ fail:
   return p;
 }
 
-/* resolve alias */
+/** @brief Resolve an alias
+ * @param track Track name (might be an alias)
+ * @return Real track name (definitely not an alias) or NULL if no such track
+ */
 const char *trackdb_resolve(const char *track) {
   DB_TXN *tid;
   const char *actual;
@@ -1599,13 +1933,20 @@ fail:
   return actual;
 }
 
+/** @brief Detect an alias
+ * @param track Track name
+ * @return Nonzero if @p track exists and is an alias
+ */
 int trackdb_isalias(const char *track) {
   const char *actual = trackdb_resolve(track);
 
   return strcmp(actual, track);
 }
 
-/* test whether a track exists (perhaps an alias) */
+/** @brief Detect whether a track exists
+ * @param track Track name (can be an alias)
+ * @return Nonzero if @p track exists (whether or not it's an alias)
+ */
 int trackdb_exists(const char *track) {
   DB_TXN *tid;
   int err;
@@ -1623,7 +1964,9 @@ fail:
   return (err == 0);
 }
 
-/* return the list of tags */
+/** @brief Return list of all known tags
+ * @return NULL-terminated tag list
+ */
 char **trackdb_alltags(void) {
   int e;
   struct vector v[1];
@@ -1654,7 +1997,7 @@ int trackdb_listkeys(DB *db, struct vector *v, DB_TXN *tid) {
   case DB_LOCK_DEADLOCK:
     return e;
   default:
-    fatal(0, "c->c_get: %s", db_strerror(e));
+    disorder_fatal(0, "c->c_get: %s", db_strerror(e));
   }
   if((e = trackdb_closecursor(c)))
     return e;
@@ -1663,6 +2006,13 @@ int trackdb_listkeys(DB *db, struct vector *v, DB_TXN *tid) {
 }
 
 /* return 1 iff sorted tag lists A and B have at least one member in common */
+/** @brief Detect intersecting tag lists
+ * @param a First list of tags (NULL-terminated)
+ * @param b Second list of tags (NULL-terminated)
+ * @return 1 if @p a and @p b have at least one member in common
+ *
+ * @p a and @p must be sorted.
+ */
 int tag_intersection(char **a, char **b) {
   int cmp;
 
@@ -1675,15 +2025,13 @@ int tag_intersection(char **a, char **b) {
   return 0;
 }
 
-static pid_t choose_pid = -1;
-static int choose_fd;
-static random_callback *choose_callback;
-static struct dynstr choose_output;
-static unsigned choose_complete;
-static int choose_status;
-#define CHOOSE_RUNNING 1
-#define CHOOSE_READING 2
-
+/** @brief Called when disorder-choose might have completed
+ * @param ev Event loop
+ * @param which @ref CHOOSE_RUNNING or @ref CHOOSE_READING
+ *
+ * Once called with both @p which values, @ref choose_callback is called
+ * (usually chosen_random_track()).
+ */
 static void choose_finished(ev_source *ev, unsigned which) {
   choose_complete |= which;
   if(choose_complete != (CHOOSE_RUNNING|CHOOSE_READING))
@@ -1696,20 +2044,35 @@ static void choose_finished(ev_source *ev, unsigned which) {
     choose_callback(ev, 0);
 }
 
-/** @brief Called when @c disorder-choose terminates */
+/** @brief Called when @c disorder-choose terminates
+ * @param ev Event loop
+ * @param pid Process ID
+ * @param status Exit status
+ * @param rusage Resource usage
+ * @param u User data
+ * @return 0
+ */
 static int choose_exited(ev_source *ev,
                          pid_t attribute((unused)) pid,
                          int status,
                          const struct rusage attribute((unused)) *rusage,
                          void attribute((unused)) *u) {
   if(status)
-    error(0, "disorder-choose %s", wstat(status));
+    disorder_error(0, "disorder-choose %s", wstat(status));
   choose_status = status;
   choose_finished(ev, CHOOSE_RUNNING);
   return 0;
 }
 
-/** @brief Called with data from @c disorder-choose pipe */
+/** @brief Called with data from @c disorder-choose pipe
+ * @param ev Event loop
+ * @param reader Reader state
+ * @param ptr Data read
+ * @param bytes Number of bytes read
+ * @param eof Set at end of file
+ * @param u User data
+ * @return 0
+ */
 static int choose_readable(ev_source *ev,
                            ev_reader *reader,
                            void *ptr,
@@ -1723,10 +2086,16 @@ static int choose_readable(ev_source *ev,
   return 0;
 }
 
+/** @brief Called when @c disorder-choose pipe errors
+ * @param ev Event loop
+ * @param errno_value Error code
+ * @param u User data
+ * @return 0
+ */
 static int choose_read_error(ev_source *ev,
                              int errno_value,
                              void attribute((unused)) *u) {
-  error(errno_value, "error reading disorder-choose pipe");
+  disorder_error(errno_value, "error reading disorder-choose pipe");
   choose_finished(ev, CHOOSE_READING);
   return 0;
 }
@@ -1760,13 +2129,21 @@ int trackdb_request_random(ev_source *ev,
   choose_complete = 0;
   if(!ev_reader_new(ev, p[0], choose_readable, choose_read_error, 0,
                     "disorder-choose reader")) /* owns p[0] */
-    fatal(0, "ev_reader_new for disorder-choose reader failed");
+    disorder_fatal(0, "ev_reader_new for disorder-choose reader failed");
   ev_child(ev, choose_pid, 0, choose_exited, 0); /* owns the subprocess */
   return 0;
 }
 
-/* get a track name given the prefs.  Set *used_db to 1 if we got the answer
- * from the prefs. */
+/** @brief Get a track name part, using prefs
+ * @param track Track name
+ * @param context Context ("display" etc)
+ * @param part Part ("album" etc)
+ * @param p Preference
+ * @param used_db Set if a preference is used
+ * @return Name part (never NULL)
+ *
+ * Used by compute_alias() and trackdb_getpart().
+ */
 static const char *getpart(const char *track,
                            const char *context,
                            const char *part,
@@ -1784,8 +2161,14 @@ static const char *getpart(const char *track,
   return result;
 }
 
-/* get a track name part, like trackname_part(), but taking the database into
- * account. */
+/** @brief Get a track name part
+ * @param track Track name
+ * @param context Context ("display" etc)
+ * @param part Part ("album" etc)
+ * @return Name part (never NULL)
+ *
+ * This is interface used by c_part().
+ */
 const char *trackdb_getpart(const char *track,
                             const char *context,
                             const char *part) {
@@ -1809,7 +2192,12 @@ fail:
   return getpart(actual, context, part, p, &used_db);
 }
 
-/* get the raw path name for @track@ (might be an alias) */
+/** @brief Get the raw (filesystem) path for @p track
+ * @param track track Track name (can be an alias)
+ * @return Raw path (never NULL)
+ *
+ * The raw path is the actual bytes that came out of readdir() etc.
+ */
 const char *trackdb_rawpath(const char *track) {
   DB_TXN *tid;
   struct kvp *t;
@@ -1835,6 +2223,19 @@ fail:
 
 /* return true if the basename of TRACK[0..TL-1], as defined by DL, matches RE.
  * If RE is a null pointer then it matches everything. */
+/** @brief Match a track against a rgeexp
+ * @param dl Length of directory part of track
+ * @param track Track name
+ * @param tl Length of track name
+ * @param re Regular expression or NULL
+ * @return Nonzero on match
+ *
+ * @p tl is the total length of @p track, @p dl is the length of the directory
+ * part (the index of the final "/").  The subject of the regexp match is the
+ * basename, i.e. the part after @p dl.
+ *
+ * If @p re is NULL then always matches.
+ */
 static int track_matches(size_t dl, const char *track, size_t tl,
                         const pcre *re) {
   int ovec[3], rc;
@@ -1847,13 +2248,21 @@ static int track_matches(size_t dl, const char *track, size_t tl,
   case PCRE_ERROR_NOMATCH: return 0;
   default:
     if(rc < 0) {
-      error(0, "pcre_exec returned %d, subject '%s'", rc, track);
+      disorder_error(0, "pcre_exec returned %d, subject '%s'", rc, track);
       return 0;
     }
     return 1;
   }
 }
 
+/** @brief Generate a list of tracks and/or directories in @p dir
+ * @param v Where to put results
+ * @param dir Directory to list
+ * @param what Bitmap of objects to return
+ * @param re Regexp to filter matches (or NULL to accept all)
+ * @param tid Owning transaction
+ * @return 0 or DB_LOCK_DEADLOCK
+ */
 static int do_list(struct vector *v, const char *dir,
                    enum trackdb_listable what, const pcre *re, DB_TXN *tid) {
   DBC *cursor;
@@ -1935,17 +2344,23 @@ static int do_list(struct vector *v, const char *dir,
     err = 0;
     break;
   case DB_LOCK_DEADLOCK:
-    error(0, "error querying database: %s", db_strerror(err));
+    disorder_error(0, "error querying database: %s", db_strerror(err));
     break;
   default:
-    fatal(0, "error querying database: %s", db_strerror(err));
+    disorder_fatal(0, "error querying database: %s", db_strerror(err));
   }
 deadlocked:
   if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
   return err;
 }
 
-/* return the directories or files below @dir@ */
+/** @brief Get the directories or files below @p dir
+ * @param dir Directory to list
+ * @param np Where to put number of results (or NULL)
+ * @param what Bitmap of objects to return
+ * @param re Regexp to filter matches (or NULL to accept all)
+ * @return List of tracks
+ */
 char **trackdb_list(const char *dir, int *np, enum trackdb_listable what,
                     const pcre *re) {
   DB_TXN *tid;
@@ -1975,7 +2390,12 @@ fail:
   return v.vec;
 }
 
-/* If S is tag:something, return something.  Else return 0. */
+/** @brief Detect a tag element in a search string
+ * @param s Element of search string
+ * @return Pointer to tag name (in @p s) if this is a tag: search, else NULL
+ *
+ * Tag searches take the form "tag:TAG".
+ */
 static const char *checktag(const char *s) {
   if(!strncmp(s, "tag:", 4))
     return s + 4;
@@ -2064,10 +2484,12 @@ char **trackdb_search(char **wordlist, int nwordlist, int *ntracks) {
       err = 0;
       break;
     case DB_LOCK_DEADLOCK:
-      error(0, "error querying %s database: %s", dbname, db_strerror(err));
+      disorder_error(0, "error querying %s database: %s",
+                     dbname, db_strerror(err));
       break;
     default:
-      fatal(0, "error querying %s database: %s", dbname, db_strerror(err));
+      disorder_fatal(0, "error querying %s database: %s",
+                     dbname, db_strerror(err));
     }
     if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
     cursor = 0;
@@ -2077,7 +2499,8 @@ char **trackdb_search(char **wordlist, int nwordlist, int *ntracks) {
       if((err = gettrackdata(v.vec[n], 0, &p, 0, 0, tid) == DB_LOCK_DEADLOCK))
         goto fail;
       else if(err) {
-        error(0, "track %s unexpected error: %s", v.vec[n], db_strerror(err));
+        disorder_error(0, "track %s unexpected error: %s",
+                       v.vec[n], db_strerror(err));
         continue;
       }
       twords = track_to_words(v.vec[n], p);
@@ -2104,7 +2527,7 @@ char **trackdb_search(char **wordlist, int nwordlist, int *ntracks) {
     trackdb_closecursor(cursor);
     cursor = 0;
     trackdb_abort_transaction(tid);
-    info("retrying search");
+    disorder_info("retrying search");
   }
   trackdb_commit_transaction(tid);
   vector_terminate(&u);
@@ -2115,6 +2538,17 @@ char **trackdb_search(char **wordlist, int nwordlist, int *ntracks) {
 
 /* trackdb_scan **************************************************************/
 
+/** @brief Visit every track
+ * @param root Root to scan or NULL for all
+ * @param callback Callback for each track
+ * @param u Passed to @p callback
+ * @param tid Owning transaction
+ * @return 0, DB_LOCK_DEADLOCK or EINTR
+ *
+ * Visits every track and calls @p callback.  @p callback will get the track
+ * data and preferences and should return 0 to continue scanning or EINTR to
+ * stop.
+ */
 int trackdb_scan(const char *root,
                  int (*callback)(const char *track,
                                  struct kvp *data,
@@ -2164,11 +2598,11 @@ int trackdb_scan(const char *root,
           prefs = 0;
           break;
         case DB_LOCK_DEADLOCK:
-          error(0, "getting prefs: %s", db_strerror(err));
+          disorder_error(0, "getting prefs: %s", db_strerror(err));
           trackdb_closecursor(cursor);
           return err;
         default:
-          fatal(0, "getting prefs: %s", db_strerror(err));
+          disorder_fatal(0, "getting prefs: %s", db_strerror(err));
         }
         /* Advance to the next track before the callback so that the callback
          * may safely delete the track */
@@ -2190,10 +2624,10 @@ int trackdb_scan(const char *root,
   case DB_NOTFOUND:
     return 0;
   case DB_LOCK_DEADLOCK:
-    error(0, "c->c_get: %s", db_strerror(err));
+    disorder_error(0, "c->c_get: %s", db_strerror(err));
     return err;
   default:
-    fatal(0, "c->c_get: %s", db_strerror(err));
+    disorder_fatal(0, "c->c_get: %s", db_strerror(err));
   }
 }
 
@@ -2229,7 +2663,7 @@ static int reap_rescan(ev_source attribute((unused)) *ev,
                        void attribute((unused)) *u) {
   if(pid == rescan_pid) rescan_pid = -1;
   if(status)
-    error(0, RESCAN": %s", wstat(status));
+    disorder_error(0, RESCAN": %s", wstat(status));
   else
     D((RESCAN" terminated: %s", wstat(status)));
   /* Our cache of file lookups is out of date now */
@@ -2259,7 +2693,7 @@ void trackdb_rescan(ev_source *ev, int recheck,
 
   if(rescan_pid != -1) {
     trackdb_add_rescanned(rescanned, ru);
-    error(0, "rescan already underway");
+    disorder_error(0, "rescan already underway");
     return;
   }
   rescan_pid = subprogram(ev, -1, RESCAN,
@@ -2277,10 +2711,13 @@ void trackdb_rescan(ev_source *ev, int recheck,
   }
 }
 
+/** @brief Cancel a rescan
+ * @return Nonzero if a rescan was cancelled
+ */
 int trackdb_rescan_cancel(void) {
   if(rescan_pid == -1) return 0;
   if(kill(rescan_pid, SIGTERM) < 0)
-    fatal(errno, "error killing rescanner");
+    disorder_fatal(errno, "error killing rescanner");
   rescan_pid = -1;
   return 1;
 }
@@ -2292,6 +2729,11 @@ int trackdb_rescan_underway(void) {
 
 /* global prefs **************************************************************/
 
+/** @brief Set a global preference
+ * @param name Global preference name
+ * @param value New value
+ * @param who Who is setting it
+ */
 void trackdb_set_global(const char *name,
                         const char *value,
                         const char *who) {
@@ -2309,20 +2751,25 @@ void trackdb_set_global(const char *name,
   /* log important state changes */
   if(!strcmp(name, "playing")) {
     state = !value || !strcmp(value, "yes");
-    info("playing %s by %s",
-         state ? "enabled" : "disabled",
-         who ? who : "-");
+    disorder_info("playing %s by %s",
+                  state ? "enabled" : "disabled",
+                  who ? who : "-");
     eventlog("state", state ? "enable_play" : "disable_play", (char *)0);
   }
   if(!strcmp(name, "random-play")) {
     state = !value || !strcmp(value, "yes");
-    info("random play %s by %s",
-         state ? "enabled" : "disabled",
-         who ? who : "-");
+    disorder_info("random play %s by %s",
+                  state ? "enabled" : "disabled",
+                  who ? who : "-");
     eventlog("state", state ? "enable_random" : "disable_random", (char *)0);
   }
 }
 
+/** @brief Set a global preference
+ * @param name Global preference name
+ * @param value New value
+ * @param tid Owning transaction
+ */
 int trackdb_set_global_tid(const char *name,
                            const char *value,
                            DB_TXN *tid) {
@@ -2343,10 +2790,14 @@ int trackdb_set_global_tid(const char *name,
     err = trackdb_globaldb->del(trackdb_globaldb, tid, &k, 0);
   if(err == DB_LOCK_DEADLOCK) return err;
   if(err)
-    fatal(0, "error updating database: %s", db_strerror(err));
+    disorder_fatal(0, "error updating database: %s", db_strerror(err));
   return 0;
 }
 
+/** @brief Get a global preference
+ * @param name Global preference name
+ * @return Value of global preference, or NULL if it's not set
+ */
 const char *trackdb_get_global(const char *name) {
   DB_TXN *tid;
   int err;
@@ -2362,6 +2813,12 @@ const char *trackdb_get_global(const char *name) {
   return r;
 }
 
+/** @brief Get a global preference
+ * @param name Global preference name
+ * @param tid Owning transaction
+ * @param rp Where to store value (will get NULL if preference not set)
+ * @return 0 or DB_LOCK_DEADLOCK
+ */
 int trackdb_get_global_tid(const char *name,
                            DB_TXN *tid,
                            const char **rp) {
@@ -2382,7 +2839,7 @@ int trackdb_get_global_tid(const char *name,
   case DB_LOCK_DEADLOCK:
     return err;
   default:
-    fatal(0, "error reading database: %s", db_strerror(err));
+    disorder_fatal(0, "error reading database: %s", db_strerror(err));
   }
 }
 
@@ -2450,7 +2907,7 @@ static char **trackdb_new_tid(int *ntracksp,
     trackdb_closecursor(c);
     return 0;
   default:
-    fatal(0, "error reading noticed.db: %s", db_strerror(err));
+    disorder_fatal(0, "error reading noticed.db: %s", db_strerror(err));
   }
   if((err = trackdb_closecursor(c)))
     return 0;                           /* deadlock */
@@ -2496,8 +2953,8 @@ static int trackdb_expire_noticed_tid(time_t earliest, DB_TXN *tid) {
       break;
     if((err = c->c_del(c, 0))) {
       if(err != DB_LOCK_DEADLOCK)
-        fatal(0, "error deleting expired noticed.db entry: %s",
-              db_strerror(err));
+        disorder_fatal(0, "error deleting expired noticed.db entry: %s",
+                       db_strerror(err));
       break;
     }
     ++count;
@@ -2505,20 +2962,24 @@ static int trackdb_expire_noticed_tid(time_t earliest, DB_TXN *tid) {
   if(err == DB_NOTFOUND)
     err = 0;
   if(err && err != DB_LOCK_DEADLOCK)
-    fatal(0, "error expiring noticed.db: %s", db_strerror(err));
+    disorder_fatal(0, "error expiring noticed.db: %s", db_strerror(err));
   ret = err;
   if((err = trackdb_closecursor(c))) {
     if(err != DB_LOCK_DEADLOCK)
-      fatal(0, "error closing cursor: %s", db_strerror(err));
+      disorder_fatal(0, "error closing cursor: %s", db_strerror(err));
     ret = err;
   }
   if(!ret && count)
-    info("expired %d tracks from noticed.db", count);
+    disorder_info("expired %d tracks from noticed.db", count);
   return ret;
 }
 
 /* tidying up ****************************************************************/
 
+/** @brief Do database garbage collection
+ *
+ * Called form periodic_database_gc().
+ */
 void trackdb_gc(void) {
   int err;
   char **logfiles;
@@ -2527,9 +2988,9 @@ void trackdb_gc(void) {
                                         config->checkpoint_kbyte,
                                         config->checkpoint_min,
                                         0)))
-    fatal(0, "trackdb_env->txn_checkpoint: %s", db_strerror(err));
+    disorder_fatal(0, "trackdb_env->txn_checkpoint: %s", db_strerror(err));
   if((err = trackdb_env->log_archive(trackdb_env, &logfiles, DB_ARCH_REMOVE)))
-    fatal(0, "trackdb_env->log_archive: %s", db_strerror(err));
+    disorder_fatal(0, "trackdb_env->log_archive: %s", db_strerror(err));
   /* This makes catastrophic recovery impossible.  However, the user can still
    * preserve the important data by using disorder-dump to snapshot their
    * prefs, and later to restore it.  This is likely to have much small
@@ -2538,7 +2999,12 @@ void trackdb_gc(void) {
 
 /* user database *************************************************************/
 
-/** @brief Return true if @p user is trusted */
+/** @brief Return true if @p user is trusted
+ * @param user User to look up
+ * @return Nonzero if they are in the 'trusted' list
+ *
+ * Now used only in upgrade from old versions.
+ */
 static int trusted(const char *user) {
   int n;
 
@@ -2548,29 +3014,16 @@ static int trusted(const char *user) {
   return n < config->trust.n;
 }
 
-/** @brief Return non-zero for a valid username
- *
- * Currently we only allow the letters and digits in ASCII.  We could be more
- * liberal than this but it is a nice simple test.  It is critical that
- * semicolons are never allowed.
+/** @brief Add a user
+ * @param user Username
+ * @param password Initial password or NULL
+ * @param rights Initial rights
+ * @param email Email address or NULL
+ * @param confirmation Confirmation string to require
+ * @param tid Owning transaction
+ * @param flags DB flags e.g. DB_NOOVERWRITE
+ * @return 0, DB_KEYEXIST or DB_LOCK_DEADLOCK
  */
-static int valid_username(const char *user) {
-  if(!*user)
-    return 0;
-  while(*user) {
-    const uint8_t c = *user++;
-    /* For now we are very strict */
-    if((c >= 'a' && c <= 'z')
-       || (c >= 'A' && c <= 'Z')
-       || (c >= '0' && c <= '9'))
-      /* ok */;
-    else
-      return 0;
-  }
-  return 1;
-}
-
-/** @brief Add a user */
 static int create_user(const char *user,
                        const char *password,
                        const char *rights,
@@ -2583,11 +3036,11 @@ static int create_user(const char *user,
 
   /* sanity check user */
   if(!valid_username(user)) {
-    error(0, "invalid username '%s'", user);
+    disorder_error(0, "invalid username '%s'", user);
     return -1;
   }
   if(parse_rights(rights, 0, 1)) {
-    error(0, "invalid rights string");
+    disorder_error(0, "invalid rights string");
     return -1;
   }
   /* data for this user */
@@ -2598,19 +3051,26 @@ static int create_user(const char *user,
     kvp_set(&k, "email", email);
   if(confirmation)
     kvp_set(&k, "confirmation", confirmation);
-  snprintf(s, sizeof s, "%jd", (intmax_t)time(0));
+  snprintf(s, sizeof s, "%jd", (intmax_t)xtime(0));
   kvp_set(&k, "created", s);
   return trackdb_putdata(trackdb_usersdb, user, k, tid, flags);
 }
 
-/** @brief Add one pre-existing user */
+/** @brief Add one pre-existing user 
+ * @param user Username
+ * @param password password
+ * @param tid Owning transaction
+ * @return 0, DB_KEYEXIST or DB_LOCK_DEADLOCK
+ *
+ * Used only in upgrade from old versions.
+ */
 static int one_old_user(const char *user, const char *password,
                         DB_TXN *tid) {
   const char *rights;
 
   /* www-data doesn't get added */
   if(!strcmp(user, "www-data")) {
-    info("not adding www-data to user database");
+    disorder_info("not adding www-data to user database");
     return 0;
   }
   /* pick rights */
@@ -2630,6 +3090,10 @@ static int one_old_user(const char *user, const char *password,
                      tid, DB_NOOVERWRITE);
 }
 
+/** @brief Upgrade old users
+ * @param tid Owning transaction
+ * @return 0 or DB_LOCK_DEADLOCK
+ */
 static int trackdb_old_users_tid(DB_TXN *tid) {
   int n;
 
@@ -2637,10 +3101,11 @@ static int trackdb_old_users_tid(DB_TXN *tid) {
     switch(one_old_user(config->allow.s[n].s[0], config->allow.s[n].s[1],
                         tid)) {
     case 0:
-      info("created user %s from 'allow' directive", config->allow.s[n].s[0]);
+      disorder_info("created user %s from 'allow' directive",
+                    config->allow.s[n].s[0]);
       break;
     case DB_KEYEXIST:
-      error(0, "user %s already exists, delete 'allow' directive",
+      disorder_error(0, "user %s already exists, delete 'allow' directive",
             config->allow.s[n].s[0]);
           /* This won't ever become fatal - eventually 'allow' will be
            * disabled. */
@@ -2674,7 +3139,7 @@ void trackdb_create_root(void) {
                                0/*email*/, 0/*confirmation*/,
                                tid, DB_NOOVERWRITE));
   if(e == 0)
-    info("created root user");
+    disorder_info("created root user");
 }
 
 /** @brief Find a user's password from the database
@@ -2715,14 +3180,15 @@ int trackdb_adduser(const char *user,
   WITH_TRANSACTION(create_user(user, password, rights, email, confirmation,
                                tid, DB_NOOVERWRITE));
   if(e) {
-    error(0, "cannot create user '%s' because they already exist", user);
+    disorder_error(0, "cannot create user '%s' because they already exist",
+                   user);
     return -1;
   } else {
     if(email)
-      info("created user '%s' with rights '%s' and email address '%s'",
-           user, rights, email);
+      disorder_info("created user '%s' with rights '%s' and email address '%s'",
+                    user, rights, email);
     else
-      info("created user '%s' with rights '%s'", user, rights);
+      disorder_info("created user '%s' with rights '%s'", user, rights);
     eventlog("user_add", user, (char *)0);
     return 0;
   }
@@ -2737,10 +3203,11 @@ int trackdb_deluser(const char *user) {
 
   WITH_TRANSACTION(trackdb_delkey(trackdb_usersdb, user, tid));
   if(e) {
-    error(0, "cannot delete user '%s' because they do not exist", user);
+    disorder_error(0, "cannot delete user '%s' because they do not exist",
+                   user);
     return -1;
   }
-  info("deleted user '%s'", user);
+  disorder_info("deleted user '%s'", user);
   eventlog("user_delete", user, (char *)0);
   return 0;
 }
@@ -2794,32 +3261,33 @@ int trackdb_edituserinfo(const char *user,
 
   if(!strcmp(key, "rights")) {
     if(!value) {
-      error(0, "cannot remove 'rights' key from user '%s'", user);
+      disorder_error(0, "cannot remove 'rights' key from user '%s'", user);
       return -1;
     }
     if(parse_rights(value, 0, 1)) {
-      error(0, "invalid rights string");
+      disorder_error(0, "invalid rights string");
       return -1;
     }
   } else if(!strcmp(key, "email")) {
     if(*value) {
       if(!email_valid(value)) {
-        error(0, "invalid email address '%s' for user '%s'", value, user);
+        disorder_error(0, "invalid email address '%s' for user '%s'",
+                       value, user);
         return -1;
       }
     } else
       value = 0;                        /* no email -> remove key */
   } else if(!strcmp(key, "created")) {
-    error(0, "cannot change creation date for user '%s'", user);
+    disorder_error(0, "cannot change creation date for user '%s'", user);
     return -1;
   } else if(strcmp(key, "password")
             && !strcmp(key, "confirmation")) {
-    error(0, "unknown user info key '%s' for user '%s'", key, user);
+    disorder_error(0, "unknown user info key '%s' for user '%s'", key, user);
     return -1;
   }
   WITH_TRANSACTION(trackdb_edituserinfo_tid(user, key, value, tid));
   if(e) {
-    error(0, "unknown user '%s'", user);
+    disorder_error(0, "unknown user '%s'", user);
     return -1;
   } else {
     eventlog("user_edit", user, key, (char *)0);
@@ -2857,18 +3325,18 @@ static int trackdb_confirm_tid(const char *user, const char *confirmation,
   if((e = trackdb_getdata(trackdb_usersdb, user, &k, tid)))
     return e;
   if(!(stored_confirmation = kvp_get(k, "confirmation"))) {
-    error(0, "already confirmed user '%s'", user);
+    disorder_error(0, "already confirmed user '%s'", user);
     /* DB claims -30,800 to -30,999 so -1 should be a safe bet */
     return -1;
   }
   if(!(rights = kvp_get(k, "rights"))) {
-    error(0, "no rights for unconfirmed user '%s'", user);
+    disorder_error(0, "no rights for unconfirmed user '%s'", user);
     return -1;
   }
   if(parse_rights(rights, rightsp, 1))
     return -1;
   if(strcmp(confirmation, stored_confirmation)) {
-    error(0, "wrong confirmation string for user '%s'", user);
+    disorder_error(0, "wrong confirmation string for user '%s'", user);
     return -1;
   }
   /* 'sall good */
@@ -2889,11 +3357,11 @@ int trackdb_confirm(const char *user, const char *confirmation,
   WITH_TRANSACTION(trackdb_confirm_tid(user, confirmation, rightsp, tid));
   switch(e) {
   case 0:
-    info("registration confirmed for user '%s'", user);
+    disorder_info("registration confirmed for user '%s'", user);
     eventlog("user_confirm", user, (char *)0);
     return 0;
   case DB_NOTFOUND:
-    error(0, "confirmation for nonexistent user '%s'", user);
+    disorder_error(0, "confirmation for nonexistent user '%s'", user);
     return -1;
   default:                              /* already reported */
     return -1;
index 0774579..1d7c8e9 100644 (file)
@@ -61,7 +61,7 @@ extern unsigned long cache_files_hits, cache_files_misses;
 #define TRACKDB_READ_ONLY 0x0020
 
 void trackdb_init(int flags);
-void trackdb_deinit(void);
+void trackdb_deinit(ev_source *ev);
 /* close/close environment */
 
 void trackdb_master(struct ev_source *ev);
@@ -184,6 +184,22 @@ void trackdb_add_rescanned(void (*rescanned)(void *ru),
                            void *ru);
 int trackdb_rescan_underway(void);
 
+int trackdb_playlist_get(const char *name,
+                         const char *who,
+                         char ***tracksp,
+                         int *ntracksp,
+                         char **sharep);
+int trackdb_playlist_set(const char *name,
+                         const char *who,
+                         char **tracks,
+                         int ntracks,
+                         const char *share);
+void trackdb_playlist_list(const char *who,
+                           char ***playlistsp,
+                           int *nplaylistsp);
+int trackdb_playlist_delete(const char *name,
+                            const char *who);
+
 #endif /* TRACKDB_H */
 
 /*
index fa40c3b..aa11e2e 100644 (file)
@@ -51,7 +51,11 @@ const char *find_track_root(const char *track) {
   const struct collection *c = find_track_collection(track);
   if(c)
     return c->root;
-  error(0, "found track in no collection '%s'", track);
+  /* Suppress this message for scratches */
+  for(int n = 0; n < config->scratch.n; ++n)
+    if(!strcmp(track, config->scratch.s[n]))
+      return 0;
+  disorder_error(0, "found track in no collection '%s'", track);
   return 0;
 }
 
index 63b881b..56f933e 100644 (file)
@@ -51,7 +51,14 @@ int compare_path_raw(const unsigned char *ap, size_t an,
 /* Comparison function for path names that groups all entries in a directory
  * together */
 
-/* Convenient wrapper for compare_path_raw */
+/** @brief Compare two paths
+ * @param ap First path
+ * @param bp Second path
+ * @return -ve, 0 or +ve for ap <, = or > bp
+ *
+ * Sorts files within a directory together.
+ * A wrapper around compare_path_raw().
+ */
 static inline int compare_path(const char *ap, const char *bp) {
   return compare_path_raw((const unsigned char *)ap, strlen(ap),
                          (const unsigned char *)bp, strlen(bp));
index d0ae448..94d2bd5 100644 (file)
 #include "log.h"
 #include "unicode.h"
 
+/** @brief Compare two tracks
+ * @param sa First sort key
+ * @param sb Second sort key
+ * @param da First display string
+ * @param db Second display string
+ * @param ta First raw track
+ * @param tb Second raw track
+ * @return -ve, 0 or +ve for a <, = or > b
+ *
+ * Tries the following comparisons until a difference is found:
+ * - case-independent comparison of sort keys
+ * - case-dependent comparison of sort keys
+ * - case-independent comparison of display strings
+ * - case-dependent comparison of display strings
+ * - case-dependent comparison of paths (see compare_path())
+ */
 int compare_tracks(const char *sa, const char *sb,
                   const char *da, const char *db,
                   const char *ta, const char *tb) {
@@ -43,6 +59,17 @@ int compare_tracks(const char *sa, const char *sb,
   return compare_path(ta, tb);
 }
 
+/** @brief Compare two paths
+ * @param ap First path
+ * @param an Length of @p ap
+ * @param bp Second path
+ * @param bn Length @p bp
+ * @return -ve, 0 or +ve for ap <, = or > bp
+ *
+ * Sorts files within a directory together.
+ *
+ * See also compare_path().
+ */
 int compare_path_raw(const unsigned char *ap, size_t an,
                     const unsigned char *bp, size_t bn) {
   /* Don't change this function!  The database sort order depends on it */
index 2f81739..b1fcc3a 100644 (file)
@@ -32,6 +32,16 @@ static int tracksort_compare(const void *a, const void *b) {
                        ea->track, eb->track);
 }
 
+/** @brief Sort tracks
+ * @param ntracks Number of tracks to sort
+ * @param tracks List of tracks
+ * @param type Comparison type
+ * @return Sorted track data
+ *
+ * Tracks are compared using compare_tracks(), with the sort key and display
+ * string set according to @p type, which should be "track" if the tracks are
+ * really tracks and "dir" if they are directories.
+ */
 struct tracksort_data *tracksort_init(int ntracks,
                                       char **tracks,
                                       const char *type) {
diff --git a/lib/uaudio-alsa.c b/lib/uaudio-alsa.c
new file mode 100644 (file)
index 0000000..d5bff35
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/uaudio-alsa.c
+ * @brief Support for ALSA backend */
+#include "common.h"
+
+#if HAVE_ALSA_ASOUNDLIB_H
+
+#include <alsa/asoundlib.h>
+
+#include "mem.h"
+#include "log.h"
+#include "uaudio.h"
+#include "configuration.h"
+
+/** @brief The current PCM handle */
+static snd_pcm_t *alsa_pcm;
+
+static const char *const alsa_options[] = {
+  "device",
+  "mixer-control",
+  "mixer-channel",
+  NULL
+};
+
+/** @brief Mixer handle */
+snd_mixer_t *alsa_mixer_handle;
+
+/** @brief Mixer control */
+static snd_mixer_elem_t *alsa_mixer_elem;
+
+/** @brief Left channel */
+static snd_mixer_selem_channel_id_t alsa_mixer_left;
+
+/** @brief Right channel */
+static snd_mixer_selem_channel_id_t alsa_mixer_right;
+
+/** @brief Minimum level */
+static long alsa_mixer_min;
+
+/** @brief Maximum level */
+static long alsa_mixer_max;
+
+/** @brief Actually play sound via ALSA */
+static size_t alsa_play(void *buffer, size_t samples, unsigned flags) {
+  /* If we're paused we just pretend.  We rely on snd_pcm_writei() blocking so
+   * we have to fake up a sleep here.  However it doesn't have to be all that
+   * accurate - in particular it's quite acceptable to greatly underestimate
+   * the required wait time.  For 'lengthy' waits we do this by the blunt
+   * instrument of halving it.  */
+  if(flags & UAUDIO_PAUSED) {
+    if(samples > 64)
+      samples /= 2;
+    const uint64_t ns = ((uint64_t)samples * 1000000000
+                         / (uaudio_rate * uaudio_channels));
+    struct timespec ts[1];
+    ts->tv_sec = ns / 1000000000;
+    ts->tv_nsec = ns % 1000000000;
+    while(nanosleep(ts, ts) < 0 && errno == EINTR)
+      ;
+    return samples;
+  }
+  int err;
+  /* ALSA wants 'frames', where frame = several concurrently played samples */
+  const snd_pcm_uframes_t frames = samples / uaudio_channels;
+
+  snd_pcm_sframes_t rc = snd_pcm_writei(alsa_pcm, buffer, frames);
+  if(rc < 0) {
+    switch(rc) {
+    case -EPIPE:
+      if((err = snd_pcm_prepare(alsa_pcm)))
+       disorder_fatal(0, "error calling snd_pcm_prepare: %d", err);
+      return 0;
+    case -EAGAIN:
+      return 0;
+    default:
+      disorder_fatal(0, "error calling snd_pcm_writei: %d", (int)rc);
+    }
+  }
+  return rc * uaudio_channels;
+}
+
+/** @brief Open the ALSA sound device */
+static void alsa_open(void) {
+  const char *device = uaudio_get("device", "default");
+  int err;
+
+  if((err = snd_pcm_open(&alsa_pcm,
+                        device,
+                        SND_PCM_STREAM_PLAYBACK,
+                        0)))
+    disorder_fatal(0, "error from snd_pcm_open: %d", err);
+  snd_pcm_hw_params_t *hwparams;
+  snd_pcm_hw_params_alloca(&hwparams);
+  if((err = snd_pcm_hw_params_any(alsa_pcm, hwparams)) < 0)
+    disorder_fatal(0, "error from snd_pcm_hw_params_any: %d", err);
+  if((err = snd_pcm_hw_params_set_access(alsa_pcm, hwparams,
+                                         SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+    disorder_fatal(0, "error from snd_pcm_hw_params_set_access: %d", err);
+  int sample_format;
+  if(uaudio_bits == 16)
+    sample_format = uaudio_signed ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U16;
+  else
+    sample_format = uaudio_signed ? SND_PCM_FORMAT_S8 : SND_PCM_FORMAT_U8;
+  if((err = snd_pcm_hw_params_set_format(alsa_pcm, hwparams,
+                                         sample_format)) < 0)
+    disorder_fatal(0, "error from snd_pcm_hw_params_set_format (%d): %d",
+          sample_format, err);
+  unsigned rate = uaudio_rate;
+  if((err = snd_pcm_hw_params_set_rate_near(alsa_pcm, hwparams, &rate, 0)) < 0)
+    disorder_fatal(0, "error from snd_pcm_hw_params_set_rate_near (%d): %d",
+          rate, err);
+  if((err = snd_pcm_hw_params_set_channels(alsa_pcm, hwparams,
+                                           uaudio_channels)) < 0)
+    disorder_fatal(0, "error from snd_pcm_hw_params_set_channels (%d): %d",
+          uaudio_channels, err);
+  if((err = snd_pcm_hw_params(alsa_pcm, hwparams)) < 0)
+    disorder_fatal(0, "error calling snd_pcm_hw_params: %d", err);
+  
+}
+
+static void alsa_start(uaudio_callback *callback,
+                      void *userdata) {
+  if(uaudio_channels != 1 && uaudio_channels != 2)
+    disorder_fatal(0, "asked for %d channels but only support 1 or 2",
+          uaudio_channels); 
+  if(uaudio_bits != 8 && uaudio_bits != 16)
+    disorder_fatal(0, "asked for %d bits/channel but only support 8 or 16",
+          uaudio_bits); 
+  alsa_open();
+  uaudio_thread_start(callback, userdata, alsa_play,
+                      32 / uaudio_sample_size,
+                      4096 / uaudio_sample_size,
+                      0);
+}
+
+static void alsa_stop(void) {
+  uaudio_thread_stop();
+  snd_pcm_close(alsa_pcm);
+  alsa_pcm = 0;
+}
+
+/** @brief Convert a level to a percentage */
+static int to_percent(long n) {
+  return (n - alsa_mixer_min) * 100 / (alsa_mixer_max - alsa_mixer_min);
+}
+
+/** @brief Convert a percentage to a level */
+static int from_percent(int n) {
+  return alsa_mixer_min + n * (alsa_mixer_max - alsa_mixer_min) / 100;
+}
+
+static void alsa_open_mixer(void) {
+  int err;
+  snd_mixer_selem_id_t *id;
+  const char *device = uaudio_get("device", "default");
+  const char *mixer = uaudio_get("mixer-control", "0");
+  const char *channel = uaudio_get("mixer-channel", "PCM");
+
+  snd_mixer_selem_id_alloca(&id);
+  if((err = snd_mixer_open(&alsa_mixer_handle, 0)))
+    disorder_fatal(0, "snd_mixer_open: %s", snd_strerror(err));
+  if((err = snd_mixer_attach(alsa_mixer_handle, device)))
+    disorder_fatal(0, "snd_mixer_attach %s: %s", device, snd_strerror(err));
+  if((err = snd_mixer_selem_register(alsa_mixer_handle,
+                                     0/*options*/, 0/*classp*/)))
+    disorder_fatal(0, "snd_mixer_selem_register %s: %s",
+          device, snd_strerror(err));
+  if((err = snd_mixer_load(alsa_mixer_handle)))
+    disorder_fatal(0, "snd_mixer_load %s: %s", device, snd_strerror(err));
+  snd_mixer_selem_id_set_name(id, channel);
+  snd_mixer_selem_id_set_index(id, atoi(mixer));
+  if(!(alsa_mixer_elem = snd_mixer_find_selem(alsa_mixer_handle, id)))
+    disorder_fatal(0, "device '%s' mixer control '%s,%s' does not exist",
+                   device, channel, mixer);
+  if(!snd_mixer_selem_has_playback_volume(alsa_mixer_elem))
+    disorder_fatal(0,
+                   "device '%s' mixer control '%s,%s' has no playback volume",
+                   device, channel, mixer);
+  if(snd_mixer_selem_is_playback_mono(alsa_mixer_elem)) {
+    alsa_mixer_left = alsa_mixer_right = SND_MIXER_SCHN_MONO;
+  } else {
+    alsa_mixer_left = SND_MIXER_SCHN_FRONT_LEFT;
+    alsa_mixer_right = SND_MIXER_SCHN_FRONT_RIGHT;
+  }
+  if(!snd_mixer_selem_has_playback_channel(alsa_mixer_elem,
+                                           alsa_mixer_left)
+     || !snd_mixer_selem_has_playback_channel(alsa_mixer_elem,
+                                              alsa_mixer_right))
+    disorder_fatal(0, "device '%s' mixer control '%s,%s' lacks required playback channels",
+                   device, channel, mixer);
+  snd_mixer_selem_get_playback_volume_range(alsa_mixer_elem,
+                                            &alsa_mixer_min, &alsa_mixer_max);
+
+}
+
+static void alsa_close_mixer(void) {
+  /* TODO alsa_mixer_elem */
+  if(alsa_mixer_handle)
+    snd_mixer_close(alsa_mixer_handle);
+}
+
+static void alsa_get_volume(int *left, int *right) {
+  long l, r;
+  int err;
+  
+  if((err = snd_mixer_selem_get_playback_volume(alsa_mixer_elem,
+                                                alsa_mixer_left, &l))
+     || (err = snd_mixer_selem_get_playback_volume(alsa_mixer_elem,
+                                                   alsa_mixer_right, &r)))
+    disorder_fatal(0, "snd_mixer_selem_get_playback_volume: %s",
+                   snd_strerror(err));
+  *left = to_percent(l);
+  *right = to_percent(r);
+}
+
+static void alsa_set_volume(int *left, int *right) {
+  long l, r;
+  int err;
+  
+  /* Set the volume */
+  if(alsa_mixer_left == alsa_mixer_right) {
+    /* Mono output - just use the loudest */
+    if((err = snd_mixer_selem_set_playback_volume
+       (alsa_mixer_elem, alsa_mixer_left,
+        from_percent(*left > *right ? *left : *right))))
+      disorder_fatal(0, "snd_mixer_selem_set_playback_volume: %s",
+                     snd_strerror(err));
+  } else {
+    /* Stereo output */
+    if((err = snd_mixer_selem_set_playback_volume
+       (alsa_mixer_elem, alsa_mixer_left, from_percent(*left)))
+       || (err = snd_mixer_selem_set_playback_volume
+          (alsa_mixer_elem, alsa_mixer_right, from_percent(*right))))
+      disorder_fatal(0, "snd_mixer_selem_set_playback_volume: %s",
+                     snd_strerror(err));
+  }
+  /* Read it back to see what we ended up at */
+  if((err = snd_mixer_selem_get_playback_volume(alsa_mixer_elem,
+                                                alsa_mixer_left, &l))
+     || (err = snd_mixer_selem_get_playback_volume(alsa_mixer_elem,
+                                                   alsa_mixer_right, &r)))
+    disorder_fatal(0, "snd_mixer_selem_get_playback_volume: %s",
+                   snd_strerror(err));
+  *left = to_percent(l);
+  *right = to_percent(r);
+}
+
+static void alsa_configure(void) {
+  uaudio_set("device", config->device);
+  uaudio_set("mixer-control", config->mixer);
+  uaudio_set("mixer-channel", config->channel);
+}
+
+const struct uaudio uaudio_alsa = {
+  .name = "alsa",
+  .options = alsa_options,
+  .start = alsa_start,
+  .stop = alsa_stop,
+  .activate = uaudio_thread_activate,
+  .deactivate = uaudio_thread_deactivate,
+  .open_mixer = alsa_open_mixer,
+  .close_mixer = alsa_close_mixer,
+  .get_volume = alsa_get_volume,
+  .set_volume = alsa_set_volume,
+  .configure = alsa_configure
+};
+
+#endif
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/uaudio-apis.c b/lib/uaudio-apis.c
new file mode 100644 (file)
index 0000000..568ae36
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file lib/uaudio-apis.c
+ * @brief Audio API list
+ */
+
+#include "common.h"
+#include "uaudio.h"
+#include "log.h"
+
+/** @brief List of known APIs
+ *
+ * Terminated by a null pointer.
+ *
+ * The first one will be used as a default, so putting ALSA before OSS
+ * constitutes a policy decision.
+ */
+const struct uaudio *const uaudio_apis[] = {
+#if HAVE_COREAUDIO_AUDIOHARDWARE_H
+  &uaudio_coreaudio,
+#endif  
+#if HAVE_ALSA_ASOUNDLIB_H
+  &uaudio_alsa,
+#endif
+#if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
+  &uaudio_oss,
+#endif
+  &uaudio_rtp,
+  &uaudio_command,
+  NULL,
+};
+
+/** @brief Look up an audio API by name */
+const struct uaudio *uaudio_find(const char *name) {
+  int n;
+
+  for(n = 0; uaudio_apis[n]; ++n)
+    if(!strcmp(uaudio_apis[n]->name, name))
+      return uaudio_apis[n];
+  if(!strcmp(name, "network"))
+    return &uaudio_rtp;
+  disorder_fatal(0, "cannot find audio API '%s'", name);
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/uaudio-command.c b/lib/uaudio-command.c
new file mode 100644 (file)
index 0000000..1c29c16
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2005, 2006, 2007, 2009 Richard Kettlewell
+ * Portions (C) 2007 Mark Wooding
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/uaudio-command.c
+ * @brief Support for commmand backend
+ *
+ * We use the code in @ref lib/uaudio-schedule.c to ensure that we write at
+ * approximately the 'real' rate.  For disorder-playrtp this isn't very useful
+ * (thought it might reduce the size of various buffers downstream of us) but
+ * when run from the speaker it means that pausing stands a chance of working.
+ */
+#include "common.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "syscalls.h"
+#include "log.h"
+#include "mem.h"
+#include "wstat.h"
+#include "uaudio.h"
+#include "configuration.h"
+
+/** @brief Pipe to subprocess */
+static int command_fd;
+
+/** @brief Child process ID */
+static pid_t command_pid;
+
+/** @brief Whether to suspend on pause */
+static int command_suspend_on_pause;
+
+static const char *const command_options[] = {
+  "command",
+  "pause-mode",
+  NULL
+};
+
+/** @brief Close pipe and wait for subprocess to terminate */
+static void command_wait(void) {
+  int w;
+  pid_t rc;
+  char *ws;
+
+  close(command_fd);
+  while((rc = waitpid(command_pid, &w, 0) < 0 && errno == EINTR))
+    ;
+  if(rc < 0)
+    disorder_fatal(errno, "waitpid");
+  if(w) {
+    ws = wstat(w);
+    disorder_error(0, "command subprocess %s", ws);
+    xfree(ws);
+  }
+}
+
+/** @brief Create subprocess */ 
+static void command_open(void) {
+  int pfd[2];
+  const char *command;
+
+  if(!(command = uaudio_get("command", NULL)))
+    disorder_fatal(0, "'command' not set");
+  xpipe(pfd);
+  command_pid = xfork();
+  if(!command_pid) {
+    exitfn = _exit;
+    signal(SIGPIPE, SIG_DFL);
+    xdup2(pfd[0], 0);
+    close(pfd[0]);
+    close(pfd[1]);
+    /* TODO it would be nice to set some environment variables given the sample
+     * format.  The original intended model is that you adapt DisOrder to the
+     * command you run but it'd be nice to support the opposite. */
+    execl("/bin/sh", "sh", "-c", command, (char *)0);
+    disorder_fatal(errno, "error executing /bin/sh");
+  }
+  close(pfd[0]);
+  command_fd = pfd[1];
+}
+
+/** @brief Send audio data to subprocess */
+static size_t command_play(void *buffer, size_t nsamples, unsigned flags) {
+  uaudio_schedule_sync();
+  /* If we're pausing and want that to be represented by stopping writing, we
+   * just pretend */
+  if((flags & UAUDIO_PAUSED) && command_suspend_on_pause)
+    return nsamples;
+  const size_t bytes = nsamples * uaudio_sample_size;
+  int written = write(command_fd, buffer, bytes);
+  if(written < 0) {
+    switch(errno) {
+    case EINTR:
+      return 0;                        /* will retry */
+    case EPIPE:
+      disorder_error(0, "audio command subprocess terminated");
+      command_wait();
+      command_open();
+      return 0;                        /* will retry */
+    default:
+      disorder_fatal(errno, "error writing to audio command subprocess");
+    }
+  }
+  /* TODO what if we write a partial sample? Actually reasonably unlikely but
+   * not impossible.  Maybe someone who actually uses this backend should sort
+   * it out. */
+  const size_t written_samples = written / uaudio_sample_size;
+  uaudio_schedule_sent(written_samples);
+  return written_samples;
+}
+
+static void command_start(uaudio_callback *callback,
+                      void *userdata) {
+  const char *pausemode = uaudio_get("pause-mode", "silence");
+  unsigned flags = 0;
+
+  if(!strcmp(pausemode, "silence"))
+    command_suspend_on_pause = 0;
+  else if(!strcmp(pausemode, "suspend"))
+    command_suspend_on_pause = 1;
+  else
+    disorder_fatal(0, "unknown pause mode '%s'", pausemode);
+  command_open();
+  uaudio_schedule_init();
+  uaudio_thread_start(callback,
+                      userdata,
+                      command_play,
+                      uaudio_channels,
+                     4096 / uaudio_sample_size,
+                      flags);
+}
+
+static void command_stop(void) {
+  uaudio_thread_stop();
+  command_wait();
+}
+
+static void command_configure(void) {
+  uaudio_set("command", config->speaker_command);
+  uaudio_set("pause-mode", config->pause_mode);
+}
+
+const struct uaudio uaudio_command = {
+  .name = "command",
+  .options = command_options,
+  .start = command_start,
+  .stop = command_stop,
+  .activate = uaudio_thread_activate,
+  .deactivate = uaudio_thread_deactivate,
+  .configure = command_configure,
+};
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/uaudio-coreaudio.c b/lib/uaudio-coreaudio.c
new file mode 100644 (file)
index 0000000..73cc287
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/uaudio-coreaudio.c
+ * @brief Support for Core Audio backend */
+#include "common.h"
+
+#if HAVE_COREAUDIO_AUDIOHARDWARE_H
+
+#include "coreaudio.h"
+#include "uaudio.h"
+#include "mem.h"
+#include "log.h"
+#include "syscalls.h"
+#include "configuration.h"
+
+/** @brief Callback to request sample data */
+static uaudio_callback *coreaudio_callback;
+
+/** @brief Userdata for @ref coreaudio_callback */
+static void *coreaudio_userdata;
+
+/** @brief Core Audio device ID */
+static AudioDeviceID coreaudio_adid;
+
+/** @brief Core Audio option names */
+static const char *const coreaudio_options[] = {
+  "device",
+  NULL
+};
+
+/** @brief Callback from Core Audio
+ *
+ * Core Audio demands floating point samples but we provide integers.
+ * So there is a conversion step in here.
+ */
+static OSStatus coreaudio_adioproc
+    (AudioDeviceID attribute((unused)) inDevice,
+     const AudioTimeStamp attribute((unused)) *inNow,
+     const AudioBufferList attribute((unused)) *inInputData,
+     const AudioTimeStamp attribute((unused)) *inInputTime,
+     AudioBufferList *outOutputData,
+     const AudioTimeStamp attribute((unused)) *inOutputTime,
+     void attribute((unused)) *inClientData) {
+  /* Number of buffers we must fill */
+  unsigned nbuffers = outOutputData->mNumberBuffers;
+  /* Pointer to buffer to fill */
+  AudioBuffer *ab = outOutputData->mBuffers;
+  
+  while(nbuffers > 0) {
+    /* Where to store converted sample data */
+    float *samples = ab->mData;
+    /* Number of samples left to fill */
+    size_t nsamples = ab->mDataByteSize / sizeof (float);
+
+    while(nsamples > 0) {
+      /* Integer-format input buffer */
+      unsigned char input[1024];
+      const size_t maxsamples = sizeof input / uaudio_sample_size;
+      /* How many samples we'll ask for */
+      const size_t ask = nsamples > maxsamples ? maxsamples : nsamples;
+      /* How many we get */
+      int got;
+
+      got = coreaudio_callback(input, ask, coreaudio_userdata);
+      /* Convert the samples and store in the output buffer */
+      nsamples -= got;
+      if(uaudio_signed) {
+        if(uaudio_bits == 16) {
+          const int16_t *ptr = (int16_t *)input;
+          while(got > 0) {
+            --got;
+            *samples++ = *ptr++ * (0.5 / 32767);
+          }
+        } else {
+          const int8_t *ptr = (int8_t *)input;
+          while(got > 0) {
+            --got;
+            *samples++ = *ptr++ * (0.5 / 127);
+          }
+        }
+      } else {
+        if(uaudio_bits == 16) {
+          const uint16_t *ptr = (uint16_t *)input;
+          while(got > 0) {
+            --got;
+            *samples++ = ((int)*ptr++ - 32768) * (0.5 / 32767);
+          }
+        } else {
+          const uint8_t *ptr = (uint8_t *)input;
+          while(got > 0) {
+            --got;
+            *samples++ = ((int)*ptr++ - 128) * (0.5 / 127);
+          }
+        }
+      }
+    }
+    /* Move on to the next buffer */
+    ++ab;
+    --nbuffers;
+  }
+  return 0;
+}
+
+static void coreaudio_start(uaudio_callback *callback,
+                            void *userdata) {
+  OSStatus status;
+  UInt32 propertySize;
+  AudioStreamBasicDescription asbd;
+  const char *device;
+
+  if(uaudio_bits != 8 && uaudio_bits != 16)
+    disorder_fatal(0, "asked for %d bits/channel but only support 8 and 16",
+                   uaudio_bits);
+  coreaudio_callback = callback;
+  coreaudio_userdata = userdata;
+  device = uaudio_get("device", "default");
+  coreaudio_adid = coreaudio_getdevice(device);
+  /* Get the device properties */
+  propertySize = sizeof asbd;
+  status = AudioDeviceGetProperty(coreaudio_adid, 0, false,
+                                 kAudioDevicePropertyStreamFormat,
+                                 &propertySize, &asbd);
+  if(status)
+    coreaudio_fatal(status, "AudioHardwareGetProperty");
+  D(("mSampleRate       %f", asbd.mSampleRate));
+  D(("mFormatID         %08"PRIx32, (uint32_t)asbd.mFormatID));
+  D(("mFormatFlags      %08"PRIx32, (uint32_t)asbd.mFormatFlags));
+  D(("mBytesPerPacket   %08"PRIx32, (uint32_t)asbd.mBytesPerPacket));
+  D(("mFramesPerPacket  %08"PRIx32, (uint32_t)asbd.mFramesPerPacket));
+  D(("mBytesPerFrame    %08"PRIx32, (uint32_t)asbd.mBytesPerFrame));
+  D(("mChannelsPerFrame %08"PRIx32, (uint32_t)asbd.mChannelsPerFrame));
+  D(("mBitsPerChannel   %08"PRIx32, (uint32_t)asbd.mBitsPerChannel));
+  D(("mReserved         %08"PRIx32, (uint32_t)asbd.mReserved));
+  /* Check that everything adds up */
+  if(asbd.mFormatID != kAudioFormatLinearPCM)
+    disorder_fatal(0, "audio device does not support kAudioFormatLinearPCM");
+  if(asbd.mSampleRate != uaudio_rate
+     || asbd.mChannelsPerFrame != (unsigned)uaudio_channels) {
+    disorder_fatal(0, "want %dHz %d channels "
+                      "but got %gHz %"PRIu32" channels",
+                   uaudio_rate,
+                   uaudio_channels,
+                   (double)asbd.mSampleRate,
+                   (uint32_t)asbd.mChannelsPerFrame);
+  }
+  /* Add a collector callback */
+  status = AudioDeviceAddIOProc(coreaudio_adid, coreaudio_adioproc, 0);
+  if(status)
+    coreaudio_fatal(status, "AudioDeviceAddIOProc");
+}
+
+static void coreaudio_stop(void) {
+}
+
+static void coreaudio_activate(void) {
+  OSStatus status;
+
+  status = AudioDeviceStart(coreaudio_adid, coreaudio_adioproc);
+  if(status)
+    coreaudio_fatal(status, "AudioDeviceStart");
+}
+
+static void coreaudio_deactivate(void) {
+  OSStatus status;
+
+  status = AudioDeviceStop(coreaudio_adid, coreaudio_adioproc);
+  if(status)
+    coreaudio_fatal(status, "AudioDeviceStop");
+}
+
+static void coreaudio_configure(void) {
+  uaudio_set("device", config->device);
+}
+
+const struct uaudio uaudio_coreaudio = {
+  .name = "coreaudio",
+  .options = coreaudio_options,
+  .start = coreaudio_start,
+  .stop = coreaudio_stop,
+  .activate = coreaudio_activate,
+  .deactivate = coreaudio_deactivate,
+  .configure = coreaudio_configure,
+};
+
+#endif
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/uaudio-oss.c b/lib/uaudio-oss.c
new file mode 100644 (file)
index 0000000..abf0354
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/uaudio-oss.c
+ * @brief Support for OSS backend */
+#include "common.h"
+
+#if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
+
+#if HAVE_SYS_SOUNDCARD_H
+# include <sys/soundcard.h>
+#endif
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+
+#include "mem.h"
+#include "log.h"
+#include "uaudio.h"
+#include "configuration.h"
+
+#ifndef AFMT_U16_NE
+# if BYTE_ORDER == BIG_ENDIAN
+#  define AFMT_U16_NE AFMT_U16_BE
+# else
+#  define AFMT_U16_NE AFMT_U16_LE
+# endif
+#endif
+
+/* documentation does not match implementation! */
+#ifndef SOUND_MIXER_READ
+# define SOUND_MIXER_READ(x) MIXER_READ(x)
+#endif
+#ifndef SOUND_MIXER_WRITE
+# define SOUND_MIXER_WRITE(x) MIXER_WRITE(x)
+#endif
+
+static int oss_fd = -1;
+static int oss_mixer_fd = -1;
+static int oss_mixer_channel;
+
+static const char *const oss_options[] = {
+  "device",
+  "mixer-device",
+  "mixer-channel",
+  NULL
+};
+
+/** @brief Open the OSS sound device */
+static void oss_open(void) {
+  const char *device = uaudio_get("device", NULL);
+
+#if EMPEG_HOST
+  if(!device || !*device || !strcmp(device, "default"))
+    device = "/dev/audio";
+#else
+  if(!device || !*device || !strcmp(device, "default")) {
+    if(access("/dev/dsp", W_OK) == 0)
+      device = "/dev/dsp";
+    else
+      device = "/dev/audio";
+  }
+#endif
+  if((oss_fd = open(device, O_WRONLY, 0)) < 0)
+    disorder_fatal(errno, "error opening %s", device);
+#if !EMPEG_HOST
+  int stereo = (uaudio_channels == 2), format;
+  if(ioctl(oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
+    disorder_fatal(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo);
+  if(uaudio_bits == 16)
+    format = uaudio_signed ? AFMT_S16_NE : AFMT_U16_NE;
+  else
+    format = uaudio_signed ? AFMT_S8 : AFMT_U8;
+  if(ioctl(oss_fd, SNDCTL_DSP_SETFMT, &format) < 0)
+    disorder_fatal(errno, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format);
+  int rate = uaudio_rate;
+  if(ioctl(oss_fd, SNDCTL_DSP_SPEED, &rate) < 0)
+    disorder_fatal(errno, "error calling ioctl SNDCTL_DSP_SPEED %d", rate);
+  if(rate != uaudio_rate)
+    disorder_error(0, "asked for %dHz, got %dHz", uaudio_rate, rate);
+#endif
+}
+
+/** @brief Close the OSS sound device */
+static void oss_close(void) {
+  if(oss_fd != -1) {
+    close(oss_fd);
+    oss_fd = -1;
+  }
+}
+
+/** @brief Actually play sound via OSS */
+static size_t oss_play(void *buffer, size_t samples, unsigned flags) {
+  /* cf uaudio-alsa.c:alsa-play() */
+  if(flags & UAUDIO_PAUSED) {
+    if(flags & UAUDIO_PAUSE)
+      oss_close();
+    if(samples > 64)
+      samples /= 2;
+    const uint64_t ns = ((uint64_t)samples * 1000000000
+                         / (uaudio_rate * uaudio_channels));
+    struct timespec ts[1];
+    ts->tv_sec = ns / 1000000000;
+    ts->tv_nsec = ns % 1000000000;
+    while(nanosleep(ts, ts) < 0 && errno == EINTR)
+      ;
+    return samples;
+  }
+  if(flags & UAUDIO_RESUME)
+    oss_open();
+  const size_t bytes = samples * uaudio_sample_size;
+  int rc = write(oss_fd, buffer, bytes);
+  if(rc < 0)
+    disorder_fatal(errno, "error writing to sound device");
+  return rc / uaudio_sample_size;
+}
+
+static void oss_start(uaudio_callback *callback,
+                      void *userdata) {
+  if(uaudio_channels != 1 && uaudio_channels != 2)
+    disorder_fatal(0, "asked for %d channels but only support 1 or 2",
+          uaudio_channels); 
+  if(uaudio_bits != 8 && uaudio_bits != 16)
+    disorder_fatal(0, "asked for %d bits/channel but only support 8 or 16",
+          uaudio_bits); 
+#if EMPEG_HOST
+  /* Very specific buffer size requirements here apparently */
+  uaudio_thread_start(callback, userdata, oss_play, 
+                      4608 / uaudio_sample_size,
+                      4608 / uaudio_sample_size,
+                      0);
+#else
+  /* We could SNDCTL_DSP_GETBLKSIZE but only when the device is already open,
+   * which is kind of inconvenient.  We go with 1-4Kbyte for now. */
+  uaudio_thread_start(callback, userdata, oss_play, 
+                      32 / uaudio_sample_size,
+                      4096 / uaudio_sample_size,
+                      0);
+#endif
+}
+
+static void oss_stop(void) {
+  uaudio_thread_stop();
+  oss_close();                          /* might not have been paused */
+}
+
+/** @brief Channel names */
+static const char *oss_channels[] = SOUND_DEVICE_NAMES;
+
+static int oss_mixer_find_channel(const char *channel) {
+  if(!channel[strspn(channel, "0123456789")])
+    return atoi(channel);
+  else {
+    for(unsigned n = 0; n < sizeof oss_channels / sizeof *oss_channels; ++n)
+      if(!strcmp(oss_channels[n], channel))
+       return n;
+    return -1;
+  }
+}  
+
+static void oss_open_mixer(void) {
+  const char *mixer = uaudio_get("mixer-device", "/dev/mixer");
+  /* TODO infer mixer-device from device */
+  if((oss_mixer_fd = open(mixer, O_RDWR, 0)) < 0)
+    disorder_fatal(errno, "error opening %s", mixer);
+  const char *channel = uaudio_get("mixer-channel", "pcm");
+  oss_mixer_channel = oss_mixer_find_channel(channel);
+  if(oss_mixer_channel < 0)
+    disorder_fatal(0, "no such channel as '%s'", channel);
+}
+
+static void oss_close_mixer(void) {
+  close(oss_mixer_fd);
+  oss_mixer_fd = -1;
+}
+
+static void oss_get_volume(int *left, int *right) {
+  int r;
+
+  *left = *right = 0;
+  if(ioctl(oss_mixer_fd, SOUND_MIXER_READ(oss_mixer_channel), &r) < 0)
+    disorder_error(errno, "error getting volume");
+  else {
+    *left = r & 0xff;
+    *right = (r >> 8) & 0xff;
+  }
+}
+
+static void oss_set_volume(int *left, int *right) {
+  int r =  (*left & 0xff) + (*right & 0xff) * 256;
+  if(ioctl(oss_mixer_fd, SOUND_MIXER_WRITE(oss_mixer_channel), &r) == -1)
+    disorder_error(errno, "error setting volume");
+  else if(ioctl(oss_mixer_fd, SOUND_MIXER_READ(oss_mixer_channel), &r) < 0)
+    disorder_error(errno, "error getting volume");
+  else {
+    *left = r & 0xff;
+    *right = (r >> 8) & 0xff;
+  }
+}
+
+static void oss_configure(void) {
+  uaudio_set("device", config->device);
+  uaudio_set("mixer-device", config->mixer);
+  uaudio_set("mixer-channel", config->channel);
+}
+
+const struct uaudio uaudio_oss = {
+  .name = "oss",
+  .options = oss_options,
+  .start = oss_start,
+  .stop = oss_stop,
+  .activate = uaudio_thread_activate,
+  .deactivate = uaudio_thread_deactivate,
+  .open_mixer = oss_open_mixer,
+  .close_mixer = oss_close_mixer,
+  .get_volume = oss_get_volume,
+  .set_volume = oss_set_volume,
+  .configure = oss_configure,
+};
+
+#endif
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/uaudio-rtp.c b/lib/uaudio-rtp.c
new file mode 100644 (file)
index 0000000..c8e2ef2
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/uaudio-rtp.c
+ * @brief Support for RTP network play backend */
+#include "common.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <gcrypt.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/uio.h>
+
+#include "uaudio.h"
+#include "mem.h"
+#include "log.h"
+#include "syscalls.h"
+#include "rtp.h"
+#include "addr.h"
+#include "ifreq.h"
+#include "timeval.h"
+#include "configuration.h"
+
+/** @brief Bytes to send per network packet
+ *
+ * This is the maximum number of bytes we pass to write(2); to determine actual
+ * packet sizes, add a UDP header and an IP header (and a link layer header if
+ * it's the link layer size you care about).
+ *
+ * Don't make this too big or arithmetic will start to overflow.
+ */
+#define NETWORK_BYTES (1500-8/*UDP*/-40/*IP*/-8/*conservatism*/)
+
+/** @brief RTP payload type */
+static int rtp_payload;
+
+/** @brief RTP output socket */
+static int rtp_fd;
+
+/** @brief RTP SSRC */
+static uint32_t rtp_id;
+
+/** @brief Base for timestamp */
+static uint32_t rtp_base;
+
+/** @brief RTP sequence number */
+static uint16_t rtp_sequence;
+
+/** @brief Network error count
+ *
+ * If too many errors occur in too short a time, we give up.
+ */
+static int rtp_errors;
+
+/** @brief Set while paused */
+static volatile int rtp_paused;
+
+static const char *const rtp_options[] = {
+  "rtp-destination",
+  "rtp-destination-port",
+  "rtp-source",
+  "rtp-source-port",
+  "multicast-ttl",
+  "multicast-loop",
+  NULL
+};
+
+static void rtp_get_netconfig(const char *af,
+                              const char *addr,
+                              const char *port,
+                              struct netaddress *na) {
+  char *vec[3];
+  
+  vec[0] = uaudio_get(af, NULL);
+  vec[1] = uaudio_get(addr, NULL);
+  vec[2] = uaudio_get(port, NULL);
+  if(!*vec)
+    na->af = -1;
+  else
+    if(netaddress_parse(na, 3, vec))
+      disorder_fatal(0, "invalid RTP address");
+}
+
+static void rtp_set_netconfig(const char *af,
+                              const char *addr,
+                              const char *port,
+                              const struct netaddress *na) {
+  uaudio_set(af, NULL);
+  uaudio_set(addr, NULL);
+  uaudio_set(port, NULL);
+  if(na->af != -1) {
+    int nvec;
+    char **vec;
+
+    netaddress_format(na, &nvec, &vec);
+    if(nvec > 0) {
+      uaudio_set(af, vec[0]);
+      xfree(vec[0]);
+    }
+    if(nvec > 1) {
+      uaudio_set(addr, vec[1]);
+      xfree(vec[1]);
+    }
+    if(nvec > 2) {
+      uaudio_set(port, vec[2]);
+      xfree(vec[2]);
+    }
+    xfree(vec);
+  }
+}
+
+static size_t rtp_play(void *buffer, size_t nsamples, unsigned flags) {
+  struct rtp_header header;
+  struct iovec vec[2];
+
+#if 0
+  if(flags & (UAUDIO_PAUSE|UAUDIO_RESUME))
+    fprintf(stderr, "rtp_play %zu samples%s%s%s%s\n", nsamples,
+            flags & UAUDIO_PAUSE ? " UAUDIO_PAUSE" : "",
+            flags & UAUDIO_RESUME ? " UAUDIO_RESUME" : "",
+            flags & UAUDIO_PLAYING ? " UAUDIO_PLAYING" : "",
+            flags & UAUDIO_PAUSED ? " UAUDIO_PAUSED" : "");
+#endif
+          
+  /* We do as much work as possible before checking what time it is */
+  /* Fill out header */
+  header.vpxcc = 2 << 6;              /* V=2, P=0, X=0, CC=0 */
+  header.seq = htons(rtp_sequence++);
+  header.ssrc = rtp_id;
+  header.mpt = rtp_payload;
+  /* If we've come out of a pause, set the marker bit */
+  if(flags & UAUDIO_RESUME)
+    header.mpt |= 0x80;
+#if !WORDS_BIGENDIAN
+  /* Convert samples to network byte order */
+  uint16_t *u = buffer, *const limit = u + nsamples;
+  while(u < limit) {
+    *u = htons(*u);
+    ++u;
+  }
+#endif
+  vec[0].iov_base = (void *)&header;
+  vec[0].iov_len = sizeof header;
+  vec[1].iov_base = buffer;
+  vec[1].iov_len = nsamples * uaudio_sample_size;
+  const uint32_t timestamp = uaudio_schedule_sync();
+  header.timestamp = htonl(rtp_base + (uint32_t)timestamp);
+  /* If we're paused don't actually end a packet, we just pretend */
+  if(flags & UAUDIO_PAUSED) {
+    uaudio_schedule_sent(nsamples);
+    return nsamples;
+  }
+  int written_bytes;
+  do {
+    written_bytes = writev(rtp_fd, vec, 2);
+  } while(written_bytes < 0 && errno == EINTR);
+  if(written_bytes < 0) {
+    disorder_error(errno, "error transmitting audio data");
+    ++rtp_errors;
+    if(rtp_errors == 10)
+      disorder_fatal(0, "too many audio tranmission errors");
+    return 0;
+  } else
+    rtp_errors /= 2;                    /* gradual decay */
+  /* TODO what can we sensibly do about short writes here?  Really that's just
+   * an error and we ought to be using smaller packets. */
+  uaudio_schedule_sent(nsamples);
+  return nsamples;
+}
+
+static void rtp_open(void) {
+  struct addrinfo *res, *sres;
+  static const int one = 1;
+  int sndbuf, target_sndbuf = 131072;
+  socklen_t len;
+  struct netaddress dst[1], src[1];
+  
+  /* Get configuration */
+  rtp_get_netconfig("rtp-destination-af",
+                    "rtp-destination",
+                    "rtp-destination-port",
+                    dst);
+  rtp_get_netconfig("rtp-source-af",
+                    "rtp-source",
+                    "rtp-source-port",
+                    src);
+  /* ...microseconds */
+
+  /* Resolve addresses */
+  res = netaddress_resolve(dst, 0, IPPROTO_UDP);
+  if(!res)
+    exit(-1);
+  if(src->af != -1) {
+    sres = netaddress_resolve(src, 1, IPPROTO_UDP);
+    if(!sres)
+      exit(-1);
+  } else
+    sres = 0;
+  /* Create the socket */
+  if((rtp_fd = socket(res->ai_family,
+                      res->ai_socktype,
+                      res->ai_protocol)) < 0)
+    disorder_fatal(errno, "error creating broadcast socket");
+  if(multicast(res->ai_addr)) {
+    /* Enable multicast options */
+    const int ttl = atoi(uaudio_get("multicast-ttl", "1"));
+    const int loop = !strcmp(uaudio_get("multicast-loop", "yes"), "yes");
+    switch(res->ai_family) {
+    case PF_INET: {
+      if(setsockopt(rtp_fd, IPPROTO_IP, IP_MULTICAST_TTL,
+                    &ttl, sizeof ttl) < 0)
+        disorder_fatal(errno, "error setting IP_MULTICAST_TTL on multicast socket");
+      if(setsockopt(rtp_fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+                    &loop, sizeof loop) < 0)
+        disorder_fatal(errno, "error setting IP_MULTICAST_LOOP on multicast socket");
+      break;
+    }
+    case PF_INET6: {
+      if(setsockopt(rtp_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+                    &ttl, sizeof ttl) < 0)
+        disorder_fatal(errno, "error setting IPV6_MULTICAST_HOPS on multicast socket");
+      if(setsockopt(rtp_fd, IPPROTO_IP, IPV6_MULTICAST_LOOP,
+                    &loop, sizeof loop) < 0)
+        disorder_fatal(errno, "error setting IPV6_MULTICAST_LOOP on multicast socket");
+      break;
+    }
+    default:
+      disorder_fatal(0, "unsupported address family %d", res->ai_family);
+    }
+    disorder_info("multicasting on %s TTL=%d loop=%s", 
+                  format_sockaddr(res->ai_addr), ttl, loop ? "yes" : "no");
+  } else {
+    struct ifaddrs *ifs;
+
+    if(getifaddrs(&ifs) < 0)
+      disorder_fatal(errno, "error calling getifaddrs");
+    while(ifs) {
+      /* (At least on Darwin) IFF_BROADCAST might be set but ifa_broadaddr
+       * still a null pointer.  It turns out that there's a subsequent entry
+       * for he same interface which _does_ have ifa_broadaddr though... */
+      if((ifs->ifa_flags & IFF_BROADCAST)
+         && ifs->ifa_broadaddr
+         && sockaddr_equal(ifs->ifa_broadaddr, res->ai_addr))
+        break;
+      ifs = ifs->ifa_next;
+    }
+    if(ifs) {
+      if(setsockopt(rtp_fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0)
+        disorder_fatal(errno, "error setting SO_BROADCAST on broadcast socket");
+      disorder_info("broadcasting on %s (%s)", 
+           format_sockaddr(res->ai_addr), ifs->ifa_name);
+    } else
+      disorder_info("unicasting on %s", format_sockaddr(res->ai_addr));
+  }
+  /* Enlarge the socket buffer */
+  len = sizeof sndbuf;
+  if(getsockopt(rtp_fd, SOL_SOCKET, SO_SNDBUF,
+                &sndbuf, &len) < 0)
+    disorder_fatal(errno, "error getting SO_SNDBUF");
+  if(target_sndbuf > sndbuf) {
+    if(setsockopt(rtp_fd, SOL_SOCKET, SO_SNDBUF,
+                  &target_sndbuf, sizeof target_sndbuf) < 0)
+      disorder_error(errno, "error setting SO_SNDBUF to %d", target_sndbuf);
+    else
+      disorder_info("changed socket send buffer size from %d to %d",
+           sndbuf, target_sndbuf);
+  } else
+    disorder_info("default socket send buffer is %d", sndbuf);
+  /* We might well want to set additional broadcast- or multicast-related
+   * options here */
+  if(sres && bind(rtp_fd, sres->ai_addr, sres->ai_addrlen) < 0)
+    disorder_fatal(errno, "error binding broadcast socket to %s", 
+                   format_sockaddr(sres->ai_addr));
+  if(connect(rtp_fd, res->ai_addr, res->ai_addrlen) < 0)
+    disorder_fatal(errno, "error connecting broadcast socket to %s", 
+                   format_sockaddr(res->ai_addr));
+}
+
+static void rtp_start(uaudio_callback *callback,
+                      void *userdata) {
+  /* We only support L16 (but we do stereo and mono and will convert sign) */
+  if(uaudio_channels == 2
+     && uaudio_bits == 16
+     && uaudio_rate == 44100)
+    rtp_payload = 10;
+  else if(uaudio_channels == 1
+     && uaudio_bits == 16
+     && uaudio_rate == 44100)
+    rtp_payload = 11;
+  else
+    disorder_fatal(0, "asked for %d/%d/%d 16/44100/1 and 16/44100/2",
+                   uaudio_bits, uaudio_rate, uaudio_channels); 
+  /* Various fields are required to have random initial values by RFC3550.  The
+   * packet contents are highly public so there's no point asking for very
+   * strong randomness. */
+  gcry_create_nonce(&rtp_id, sizeof rtp_id);
+  gcry_create_nonce(&rtp_base, sizeof rtp_base);
+  gcry_create_nonce(&rtp_sequence, sizeof rtp_sequence);
+  rtp_open();
+  uaudio_schedule_init();
+  uaudio_thread_start(callback,
+                      userdata,
+                      rtp_play,
+                      256 / uaudio_sample_size,
+                      (NETWORK_BYTES - sizeof(struct rtp_header))
+                      / uaudio_sample_size,
+                      0);
+}
+
+static void rtp_stop(void) {
+  uaudio_thread_stop();
+  close(rtp_fd);
+  rtp_fd = -1;
+}
+
+static void rtp_configure(void) {
+  char buffer[64];
+
+  rtp_set_netconfig("rtp-destination-af",
+                    "rtp-destination",
+                    "rtp-destination-port", &config->broadcast);
+  rtp_set_netconfig("rtp-source-af",
+                    "rtp-source",
+                    "rtp-source-port", &config->broadcast_from);
+  snprintf(buffer, sizeof buffer, "%ld", config->multicast_ttl);
+  uaudio_set("multicast-ttl", buffer);
+  uaudio_set("multicast-loop", config->multicast_loop ? "yes" : "no");
+}
+
+const struct uaudio uaudio_rtp = {
+  .name = "rtp",
+  .options = rtp_options,
+  .start = rtp_start,
+  .stop = rtp_stop,
+  .activate = uaudio_thread_activate,
+  .deactivate = uaudio_thread_deactivate,
+  .configure = rtp_configure,
+};
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/uaudio-schedule.c b/lib/uaudio-schedule.c
new file mode 100644 (file)
index 0000000..c42de57
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/uaudio-schedule.c
+ * @brief Scheduler for RTP and command backends
+ *
+ * These functions ensure that audio is only written at approximately the rate
+ * it should play at, allowing pause to function properly.
+ *
+ * OSS and ALSA we expect to be essentially synchronous (though we could use
+ * this code if they don't play nicely).  Core Audio sorts out its own timing
+ * issues itself.
+ *
+ * The sequence numbers are intended for RTP's use but it's more convenient to
+ * maintain them here.
+ *
+ * The basic idea:
+ * - we maintain a base time
+ * - we calculate from this how many samples SHOULD have been sent by now
+ * - we compare this with the number of samples sent so far
+ * - we use this to wait until we're ready to send something
+ * - it's up to the caller to send nothing, or send 0s, if it's supposed to
+ *   be paused
+ *
+ * An implication of this is that the caller must still call
+ * uaudio_schedule_sync() when deactivated (paused) and pretend to send 0s.
+ */
+
+#include "common.h"
+
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#include "uaudio.h"
+#include "mem.h"
+#include "log.h"
+#include "syscalls.h"
+#include "timeval.h"
+
+/** @brief Sample timestamp
+ *
+ * This is the timestamp that will be used on the next outbound packet.
+ *
+ * The timestamp in an RTP packet header is only 32 bits wide.  With 44100Hz
+ * stereo, that only gives about half a day before wrapping, which is not
+ * particularly convenient for certain debugging purposes.  Therefore the
+ * timestamp is maintained as a 64-bit integer, giving around six million years
+ * before wrapping, and truncated to 32 bits when transmitting.
+ */
+static uint64_t timestamp;
+
+/** @brief Base time
+ *
+ * This is the base time that corresponds to a timestamp of 0.
+ */
+struct timeval base;
+
+/** @brief Synchronize playback operations against real time
+ * @return Sample number
+ *
+ */
+uint32_t uaudio_schedule_sync(void) {
+  const unsigned rate = uaudio_rate * uaudio_channels;
+  struct timeval now;
+
+  xgettimeofday(&now, NULL);
+  /* If we're just starting then we might as well send as much as possible
+   * straight away. */
+  if(!base.tv_sec) {
+    base = now;
+    return timestamp;
+  }
+  /* Calculate how many microseconds ahead of the base time we are */
+  uint64_t us = tvsub_us(now, base);
+  /* Calculate how many samples that is */
+  uint64_t samples = us * rate / 1000000;
+  /* So...
+   *
+   * We've actually sent 'timestamp' samples so far.
+   *
+   * We OUGHT to have sent 'samples' samples so far.
+   *
+   * Suppose it's the SECOND call.  timestamp will be (say) 716.  'samples'
+   * will be (say) 10 - there's been a bit of scheduling delay.  So in that
+   * case we should wait for 716-10=706 samples worth of time before we can
+   * even send one sample.
+   *
+   * So we wait that long and send our 716 samples.
+   *
+   * On the next call we'll have timestamp=1432 and samples=726, say.  So we
+   * wait and send again.
+   *
+   * On the next call there's been a bit of a delay.  timestamp=2148 but
+   * samples=2200.  So we send our 716 samples immediately.
+   *
+   * If the delay had been longer we might sent further packets back to back to
+   * make up for it.
+   *
+   * Now timestamp=2864 and samples=2210 (say).  Now we're back to waiting.
+   */
+  if(samples < timestamp) {
+    /* We should delay a bit */
+    int64_t wait_samples = timestamp - samples;
+    int64_t wait_ns = wait_samples * 1000000000 / rate;
+    
+    struct timespec ts[1];
+    ts->tv_sec = wait_ns / 1000000000;
+    ts->tv_nsec = wait_ns % 1000000000;
+#if 0
+    fprintf(stderr,
+            "samples=%8"PRIu64" timestamp=%8"PRIu64" wait=%"PRId64" (%"PRId64"ns)\n",
+            samples, timestamp, wait_samples, wait_ns);
+#endif
+    while(nanosleep(ts, ts) < 0 && errno == EINTR)
+      ;
+  } else {
+#if 0
+    fprintf(stderr, "samples=%8"PRIu64" timestamp=%8"PRIu64"\n",
+            samples, timestamp);
+#endif
+  }
+  /* If samples >= timestamp then it's time, or gone time, to play the
+   * timestamp'th sample.  So we return immediately. */
+  return timestamp;
+}
+
+/** @brief Report how many samples we actually sent
+ * @param nsamples_sent Number of samples sent
+ */
+void uaudio_schedule_sent(size_t nsamples_sent) {
+  timestamp += nsamples_sent;
+}
+
+/** @brief Initialize audio scheduling
+ *
+ * Should be called from your API's @c start callback.
+ */
+void uaudio_schedule_init(void) {
+  /* uaudio_schedule_play() will spot this and choose an initial value */
+  base.tv_sec = 0;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/uaudio-thread.c b/lib/uaudio-thread.c
new file mode 100644 (file)
index 0000000..e42980c
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/uaudio-thread.c
+ * @brief Background thread for audio processing */
+#include "common.h"
+
+#include <pthread.h>
+#include <unistd.h>
+
+#include "uaudio.h"
+#include "log.h"
+#include "mem.h"
+
+/** @brief Number of buffers
+ *
+ * Must be at least 2 and should normally be at least 3.  We maintain multiple
+ * buffers so that we can read new data into one while the previous is being
+ * played.
+ */
+#define UAUDIO_THREAD_BUFFERS 4
+
+/** @brief Buffer data structure */
+struct uaudio_buffer {
+  /** @brief Pointer to sample data */
+  void *samples;
+
+  /** @brief Count of samples */
+  size_t nsamples;
+};
+
+/** @brief Input buffers
+ *
+ * This is actually a ring buffer, managed by @ref uaudio_collect_buffer and
+ * @ref uaudio_play_buffer.
+ *
+ * Initially both pointers are 0.  Whenever the pointers are equal, we
+ * interpreted this as meaning that there is no data stored at all.  A
+ * consequence of this is that maximal occupancy is when the collect point is
+ * just before the play point, so at least one buffer is always empty (hence it
+ * being good for @ref UAUDIO_THREAD_BUFFERS to be at least 3).
+ */
+static struct uaudio_buffer uaudio_buffers[UAUDIO_THREAD_BUFFERS];
+
+/** @brief Buffer to read into */
+static unsigned uaudio_collect_buffer;
+
+/** @brief Buffer to play from */
+static unsigned uaudio_play_buffer;
+
+/** @brief Collection thread ID */
+static pthread_t uaudio_collect_thread;
+
+/** @brief Playing thread ID */
+static pthread_t uaudio_play_thread;
+
+/** @brief Flags */
+static unsigned uaudio_thread_flags;
+
+static uaudio_callback *uaudio_thread_collect_callback;
+static uaudio_playcallback *uaudio_thread_play_callback;
+static void *uaudio_thread_userdata;
+static int uaudio_thread_started;
+static int uaudio_thread_collecting;
+static pthread_mutex_t uaudio_thread_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t uaudio_thread_cond = PTHREAD_COND_INITIALIZER;
+
+/** @brief Minimum number of samples per chunk */
+static size_t uaudio_thread_min;
+
+/** @brief Maximum number of samples per chunk */
+static size_t uaudio_thread_max;
+
+/** @brief Set when activated, clear when paused */
+static int uaudio_thread_activated;
+
+/** @brief Return number of buffers currently in use */
+static int uaudio_buffers_used(void) {
+  return (uaudio_collect_buffer - uaudio_play_buffer) % UAUDIO_THREAD_BUFFERS;
+}
+
+/** @brief Background thread for audio collection
+ *
+ * Collects data while activated and communicates its status via @ref
+ * uaudio_thread_collecting.
+ */
+static void *uaudio_collect_thread_fn(void attribute((unused)) *arg) {
+  pthread_mutex_lock(&uaudio_thread_lock);
+  while(uaudio_thread_started) {
+    /* Wait until we're activatd */
+    if(!uaudio_thread_activated) {
+      pthread_cond_wait(&uaudio_thread_cond, &uaudio_thread_lock);
+      continue;
+    }
+    /* We are definitely active now */
+    uaudio_thread_collecting = 1;
+    pthread_cond_broadcast(&uaudio_thread_cond);
+    while(uaudio_thread_activated) {
+      if(uaudio_buffers_used() < UAUDIO_THREAD_BUFFERS - 1) {
+        /* At least one buffer is available.  We release the lock while
+         * collecting data so that other already-filled buffers can be played
+         * without delay.  */
+        struct uaudio_buffer *const b = &uaudio_buffers[uaudio_collect_buffer];
+        pthread_mutex_unlock(&uaudio_thread_lock);
+        //fprintf(stderr, "C%d.", uaudio_collect_buffer);
+        
+        /* Keep on trying until we get the minimum required amount of data */
+        b->nsamples = 0;
+        if(uaudio_thread_activated) {
+          while(b->nsamples < uaudio_thread_min) {
+            b->nsamples += uaudio_thread_collect_callback
+              ((char *)b->samples
+               + b->nsamples * uaudio_sample_size,
+               uaudio_thread_max - b->nsamples,
+               uaudio_thread_userdata);
+          }
+        }
+        pthread_mutex_lock(&uaudio_thread_lock);
+        /* Advance to next buffer */
+        uaudio_collect_buffer = (1 + uaudio_collect_buffer) % UAUDIO_THREAD_BUFFERS;
+        /* Awaken player */
+        pthread_cond_broadcast(&uaudio_thread_cond);
+      } else
+        /* No space, wait for player */
+        pthread_cond_wait(&uaudio_thread_cond, &uaudio_thread_lock);
+    }
+    uaudio_thread_collecting = 0;
+    pthread_cond_broadcast(&uaudio_thread_cond);
+  }
+  pthread_mutex_unlock(&uaudio_thread_lock);
+  return NULL;
+}
+
+/** @brief Background thread for audio playing 
+ *
+ * This thread plays data as long as there is something to play.  So the
+ * buffers will drain to empty before deactivation completes.
+ */
+static void *uaudio_play_thread_fn(void attribute((unused)) *arg) {
+  int resync = 1;
+  unsigned last_flags = 0;
+  unsigned char zero[uaudio_thread_max * uaudio_sample_size];
+  memset(zero, 0, sizeof zero);
+
+  while(uaudio_thread_started) {
+    // If we're paused then just play silence
+    if(!uaudio_thread_activated) {
+      pthread_mutex_unlock(&uaudio_thread_lock);
+      unsigned flags = UAUDIO_PAUSED;
+      if(last_flags & UAUDIO_PLAYING)
+        flags |= UAUDIO_PAUSE;
+      uaudio_thread_play_callback(zero, uaudio_thread_max,
+                                  last_flags = flags);
+      /* We expect the play callback to block for a reasonable period */
+      pthread_mutex_lock(&uaudio_thread_lock);
+      continue;
+    }
+    const int used = uaudio_buffers_used();
+    int go;
+
+    if(resync)
+      go = (used == UAUDIO_THREAD_BUFFERS - 1);
+    else
+      go = (used > 0);
+    if(go) {
+      /* At least one buffer is filled.  We release the lock while playing so
+       * that more collection can go on. */
+      struct uaudio_buffer *const b = &uaudio_buffers[uaudio_play_buffer];
+      pthread_mutex_unlock(&uaudio_thread_lock);
+      //fprintf(stderr, "P%d.", uaudio_play_buffer);
+      size_t played = 0;
+      while(played < b->nsamples) {
+        unsigned flags = UAUDIO_PLAYING;
+        if(last_flags & UAUDIO_PAUSED)
+          flags |= UAUDIO_RESUME;
+        played += uaudio_thread_play_callback((char *)b->samples
+                                              + played * uaudio_sample_size,
+                                              b->nsamples - played,
+                                              last_flags = flags);
+      }
+      pthread_mutex_lock(&uaudio_thread_lock);
+      /* Move to next buffer */
+      uaudio_play_buffer = (1 + uaudio_play_buffer) % UAUDIO_THREAD_BUFFERS;
+      /* Awaken collector */
+      pthread_cond_broadcast(&uaudio_thread_cond);
+      resync = 0;
+    } else {
+      /* Insufficient data to play, wait for collector */
+      pthread_cond_wait(&uaudio_thread_cond, &uaudio_thread_lock);
+      /* (Still) re-synchronizing */
+      resync = 1;
+    }
+  }
+  pthread_mutex_unlock(&uaudio_thread_lock);
+  return NULL;
+}
+
+/** @brief Create background threads for audio processing 
+ * @param callback Callback to collect audio data
+ * @param userdata Passed to @p callback
+ * @param playcallback Callback to play audio data
+ * @param min Minimum number of samples to play in a chunk
+ * @param max Maximum number of samples to play in a chunk
+ * @param flags Flags (not currently used)
+ *
+ * @p callback will be called multiple times in quick succession if necessary
+ * to gather at least @p min samples.  Equally @p playcallback may be called
+ * repeatedly in quick succession to play however much was received in a single
+ * chunk.
+ */
+void uaudio_thread_start(uaudio_callback *callback,
+                        void *userdata,
+                        uaudio_playcallback *playcallback,
+                        size_t min,
+                         size_t max,
+                         unsigned flags) {
+  int e;
+  uaudio_thread_collect_callback = callback;
+  uaudio_thread_userdata = userdata;
+  uaudio_thread_play_callback = playcallback;
+  uaudio_thread_min = min;
+  uaudio_thread_max = max;
+  uaudio_thread_flags = flags;
+  uaudio_thread_started = 1;
+  uaudio_thread_activated = 0;
+  for(int n = 0; n < UAUDIO_THREAD_BUFFERS; ++n)
+    uaudio_buffers[n].samples = xcalloc_noptr(uaudio_thread_max,
+                                              uaudio_sample_size);
+  uaudio_collect_buffer = uaudio_play_buffer = 0;
+  if((e = pthread_create(&uaudio_collect_thread,
+                         NULL,
+                         uaudio_collect_thread_fn,
+                         NULL)))
+    disorder_fatal(e, "pthread_create");
+  if((e = pthread_create(&uaudio_play_thread,
+                         NULL,
+                         uaudio_play_thread_fn,
+                         NULL)))
+    disorder_fatal(e, "pthread_create");
+}
+
+/** @brief Shut down background threads for audio processing */
+void uaudio_thread_stop(void) {
+  void *result;
+
+  pthread_mutex_lock(&uaudio_thread_lock);
+  uaudio_thread_activated = 0;
+  uaudio_thread_started = 0;
+  pthread_cond_broadcast(&uaudio_thread_cond);
+  pthread_mutex_unlock(&uaudio_thread_lock);
+  pthread_join(uaudio_collect_thread, &result);
+  pthread_join(uaudio_play_thread, &result);
+  for(int n = 0; n < UAUDIO_THREAD_BUFFERS; ++n)
+    xfree(uaudio_buffers[n].samples);
+}
+
+/** @brief Activate audio output */
+void uaudio_thread_activate(void) {
+  pthread_mutex_lock(&uaudio_thread_lock);
+  uaudio_thread_activated = 1;
+  pthread_cond_broadcast(&uaudio_thread_cond);
+  pthread_mutex_unlock(&uaudio_thread_lock);
+}
+
+/** @brief Deactivate audio output */
+void uaudio_thread_deactivate(void) {
+  pthread_mutex_lock(&uaudio_thread_lock);
+  uaudio_thread_activated = 0; 
+  pthread_cond_broadcast(&uaudio_thread_cond);
+  pthread_mutex_unlock(&uaudio_thread_lock);
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/uaudio.c b/lib/uaudio.c
new file mode 100644 (file)
index 0000000..e793045
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file lib/uaudio.c
+ * @brief Uniform audio interface
+ */
+
+#include "common.h"
+#include "uaudio.h"
+#include "hash.h"
+#include "mem.h"
+#include "log.h"
+
+/** @brief Options for chosen uaudio API */
+static hash *uaudio_options;
+
+/** @brief Sample rate (Hz) */
+int uaudio_rate;
+
+/** @brief Bits per channel */
+int uaudio_bits;
+
+/** @brief Number of channels */
+int uaudio_channels;
+
+/** @brief Whether samples are signed or unsigned */
+int uaudio_signed;
+
+/** @brief Sample size in bytes
+ *
+ * NB one sample is a single point sample; up to @c uaudio_channels samples may
+ * play at the same time through different speakers.  Thus this value is
+ * independent of @ref uaudio_channels.
+ */
+size_t uaudio_sample_size;
+
+/** @brief Set a uaudio option */
+void uaudio_set(const char *name, const char *value) {
+  if(!value) {
+    if(uaudio_options)
+      hash_remove(uaudio_options, name);
+    return;
+  }
+  if(!uaudio_options)
+    uaudio_options = hash_new(sizeof(char *));
+  value = xstrdup(value);
+  hash_add(uaudio_options, name, &value, HASH_INSERT_OR_REPLACE);
+}
+
+/** @brief Get a uaudio option */
+char *uaudio_get(const char *name, const char *default_value) {
+  if(!uaudio_options)
+    return default_value ? xstrdup(default_value) : 0;
+  char **valuep = hash_find(uaudio_options, name);
+  if(!valuep)
+    return default_value ? xstrdup(default_value) : 0;
+  return xstrdup(*valuep);
+}
+
+/** @brief Set sample format 
+ * @param rate Sample rate in KHz
+ * @param channels Number of channels (i.e. 2 for stereo)
+ * @param bits Number of bits per channel (typically 8 or 16)
+ * @param signed_ True for signed samples, false for unsigned
+ *
+ * Sets @ref uaudio_rate, @ref uaudio_channels, @ref uaudio_bits, @ref
+ * uaudio_signed and @ref uaudio_sample_size.
+ *
+ * Currently there is no way to specify non-native endian formats even if the
+ * underlying API can conveniently handle them.  Actually this would be quite
+ * convenient for playrtp, so it might be added at some point.
+ *
+ * Not all APIs can support all sample formats.  Generally the @c start
+ * function will do some error checking but some may be deferred to the point
+ * the device is opened (which might be @c activate).
+ */
+void uaudio_set_format(int rate, int channels, int bits, int signed_) {
+  uaudio_rate = rate;
+  uaudio_channels = channels;
+  uaudio_bits = bits;
+  uaudio_signed = signed_;
+  uaudio_sample_size = bits / CHAR_BIT;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/uaudio.h b/lib/uaudio.h
new file mode 100644 (file)
index 0000000..6592d53
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file lib/uaudio.h
+ * @brief Uniform audio interface
+ */
+
+#ifndef UAUDIO_H
+#define UAUDIO_H
+
+extern int uaudio_rate;
+extern int uaudio_bits;
+extern int uaudio_channels;
+extern int uaudio_signed;
+extern size_t uaudio_sample_size;
+
+/** @brief Callback to get audio data
+ * @param buffer Where to put audio data
+ * @param max_samples How many samples to supply
+ * @param userdata As passed to uaudio_open()
+ * @return Number of samples filled
+ *
+ * This function should not block if possible (better to fill the buffer with
+ * 0s) and should definitely not block indefinitely.  This great caution with
+ * any locks or syscalls!  In particular avoid it taking a lock that may be
+ * held while any of the @ref uaudio members are called.
+ *
+ * If it's more convenient, it's OK to return less than the maximum number of
+ * samples (including 0) provided you expect to be called again for more
+ * samples immediately.
+ */
+typedef size_t uaudio_callback(void *buffer,
+                               size_t max_samples,
+                               void *userdata);
+
+/** @brief Callback to play audio data
+ * @param buffer Pointer to audio buffer
+ * @param samples Number of samples to play
+ * @param flags Flags word
+ * @return Number of samples played
+ *
+ * Used with uaudio_thread_start() etc.
+ *
+ * @p flags is a bitmap giving the current pause state and transitions:
+ * - @ref UAUDIO_PAUSE if this is the first call of a pause
+ * - @ref UAUDIO_RESUME if this is the first call of a resumse
+ * - @ref UAUDIO_PLAYING if this is outside a pause
+ * - @ref UAUDIO_PAUSED if this is in a pause
+ *
+ * During a pause, the sample data is guaranteed to be 0.
+ */
+typedef size_t uaudio_playcallback(void *buffer, size_t samples,
+                                   unsigned flags);
+
+/** @brief Start of a pause */
+#define UAUDIO_PAUSE 0x0001
+
+/** @brief End of a pause */
+#define UAUDIO_RESUME 0x0002
+
+/** @brief Currently playing */
+#define UAUDIO_PLAYING 0x0004
+
+/** @brief Currently paused */
+#define UAUDIO_PAUSED 0x0008
+
+/** @brief Audio API definition */
+struct uaudio {
+  /** @brief Name of this API */
+  const char *name;
+
+  /** @brief List of options, terminated by NULL */
+  const char *const *options;
+
+  /** @brief Do slow setup
+   * @param ua Handle returned by uaudio_open()
+   * @param callback Called for audio data
+   * @param userdata Passed to @p callback
+   *
+   * This does resource-intensive setup for the output device.
+   *
+   * For instance it might open mixable audio devices or network sockets.  It
+   * will create any background thread required.  However, it must not exclude
+   * other processes from outputting sound.
+   */
+  void (*start)(uaudio_callback *callback,
+                void *userdata);
+
+  /** @brief Tear down
+   * @param ua Handle returned by uaudio_open()
+   *
+   * This undoes the effect of @c start.
+   */
+  void (*stop)(void);
+
+  /** @brief Enable output
+   *
+   * A background thread will start calling @c callback as set by @c
+   * start and playing the audio data received from it.
+   */
+  void (*activate)(void);
+
+  /** @brief Disable output
+   *
+   * The background thread will stop calling @c callback.
+   */
+  void (*deactivate)(void);
+
+  /** @brief Open mixer device */
+  void (*open_mixer)(void);
+
+  /** @brief Closer mixer device */
+  void (*close_mixer)(void);
+
+  /** @brief Get volume
+   * @param left Where to put the left-channel value
+   * @param right Where to put the right-channel value
+   *
+   * 0 is silent and 100 is maximum volume.
+   */
+  void (*get_volume)(int *left, int *right);
+
+  /** @brief Set volume
+   * @param left Pointer to left-channel value (updated)
+   * @param right Pointer to right-channel value (updated)
+   *
+   * The values are updated with those actually set by the underlying system
+   * call.
+   *
+   * 0 is silent and 100 is maximum volume.
+   */
+  void (*set_volume)(int *left, int *right);
+
+  /** @brief Set configuration */
+  void (*configure)(void);
+  
+};
+
+void uaudio_set_format(int rate, int channels, int samplesize, int signed_);
+void uaudio_set(const char *name, const char *value);
+char *uaudio_get(const char *name, const char *default_value);
+void uaudio_thread_start(uaudio_callback *callback,
+                        void *userdata,
+                        uaudio_playcallback *playcallback,
+                        size_t min,
+                         size_t max,
+                         unsigned flags);
+
+void uaudio_thread_stop(void);
+void uaudio_thread_activate(void);
+void uaudio_thread_deactivate(void);
+uint32_t uaudio_schedule_sync(void);
+void uaudio_schedule_sent(size_t nsamples_sent);
+void uaudio_schedule_init(void);
+const struct uaudio *uaudio_find(const char *name);
+
+extern uint64_t uaudio_schedule_timestamp;
+extern int uaudio_schedule_reactivated;
+
+#if HAVE_COREAUDIO_AUDIOHARDWARE_H
+extern const struct uaudio uaudio_coreaudio;
+#endif
+
+#if HAVE_ALSA_ASOUNDLIB_H
+extern const struct uaudio uaudio_alsa;
+#endif
+
+#if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
+extern const struct uaudio uaudio_oss;
+#endif
+
+extern const struct uaudio uaudio_rtp;
+
+extern const struct uaudio uaudio_command;
+
+extern const struct uaudio *const uaudio_apis[];
+
+#endif /* UAUDIO_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 3adbea0..675f703 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder
- * Copyright (C) 2007 Richard Kettlewell
+ * Copyright (C) 2007, 2009 Richard Kettlewell
  *
  * 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
@@ -603,6 +603,12 @@ int utf32_iterator_grapheme_boundary(utf32_iterator it) {
   /* GB9 */
   if(gbafter == unicode_Grapheme_Break_Extend)
     return 0;
+  /* GB9a */
+  if(gbafter == unicode_Grapheme_Break_SpacingMark)
+    return 0;
+  /* GB9b */
+  if(gbbefore == unicode_Grapheme_Break_Prepend)
+    return 0;
   /* GB10 */
   return 1;
 
@@ -618,26 +624,39 @@ int utf32_iterator_grapheme_boundary(utf32_iterator it) {
  * string) and 0 otherwise.
  */
 int utf32_iterator_word_boundary(utf32_iterator it) {
-  enum unicode_Word_Break twobefore, before, after, twoafter;
+  uint32_t before, after;
+  enum unicode_Word_Break wbtwobefore, wbbefore, wbafter, wbtwoafter;
   size_t nn;
 
   /* WB1 and WB2 */
   if(it->n == 0 || it->n == it->ns)
     return 1;
+  before = it->s[it->n-1];
+  after = it->s[it->n];
   /* WB3 */
-  if(it->s[it->n-1] == 0x000D && it->s[it->n] == 0x000A)
+  if(before == 0x000D && after == 0x000A)
     return 0;
+  /* WB3a */
+  if(utf32__iterator_word_break(it, before) == unicode_Word_Break_Newline
+     || before == 0x000D
+     || before == 0x000A)
+    return 1;
+  /* WB3b */
+  if(utf32__iterator_word_break(it, after) == unicode_Word_Break_Newline
+     || after == 0x000D
+     || after == 0x000A)
+    return 1;
   /* WB4 */
   /* (!Sep) x (Extend|Format) as in UAX #29 s6.2 */
-  if(utf32__sentence_break(it->s[it->n-1]) != unicode_Sentence_Break_Sep
-     && utf32__boundary_ignorable(utf32__iterator_word_break(it, it->s[it->n])))
+  if(utf32__sentence_break(before) != unicode_Sentence_Break_Sep
+     && utf32__boundary_ignorable(utf32__iterator_word_break(it, after)))
     return 0;
   /* Gather the property values we'll need for the rest of the test taking the
    * s6.2 changes into account */
   /* First we look at the code points after the proposed boundary */
   nn = it->n;                           /* <it->ns */
-  after = utf32__iterator_word_break(it, it->s[nn++]);
-  if(!utf32__boundary_ignorable(after)) {
+  wbafter = utf32__iterator_word_break(it, it->s[nn++]);
+  if(!utf32__boundary_ignorable(wbafter)) {
     /* X (Extend|Format)* -> X */
     while(nn < it->ns
           && utf32__boundary_ignorable(utf32__iterator_word_break(it,
@@ -646,67 +665,71 @@ int utf32_iterator_word_boundary(utf32_iterator it) {
   }
   /* It's possible now that nn=ns */
   if(nn < it->ns)
-    twoafter = utf32__iterator_word_break(it, it->s[nn]);
+    wbtwoafter = utf32__iterator_word_break(it, it->s[nn]);
   else
-    twoafter = unicode_Word_Break_Other;
+    wbtwoafter = unicode_Word_Break_Other;
 
   /* We've already recorded the non-ignorable code points before the proposed
    * boundary */
-  before = utf32__iterator_word_break(it, it->last[1]);
-  twobefore = utf32__iterator_word_break(it, it->last[0]);
+  wbbefore = utf32__iterator_word_break(it, it->last[1]);
+  wbtwobefore = utf32__iterator_word_break(it, it->last[0]);
 
   /* WB5 */
-  if(before == unicode_Word_Break_ALetter
-     && after == unicode_Word_Break_ALetter)
+  if(wbbefore == unicode_Word_Break_ALetter
+     && wbafter == unicode_Word_Break_ALetter)
     return 0;
   /* WB6 */
-  if(before == unicode_Word_Break_ALetter
-     && after == unicode_Word_Break_MidLetter
-     && twoafter == unicode_Word_Break_ALetter)
+  if(wbbefore == unicode_Word_Break_ALetter
+     && (wbafter == unicode_Word_Break_MidLetter
+         || wbafter == unicode_Word_Break_MidNumLet)
+     && wbtwoafter == unicode_Word_Break_ALetter)
     return 0;
   /* WB7 */
-  if(twobefore == unicode_Word_Break_ALetter
-     && before == unicode_Word_Break_MidLetter
-     && after == unicode_Word_Break_ALetter)
+  if(wbtwobefore == unicode_Word_Break_ALetter
+     && (wbbefore == unicode_Word_Break_MidLetter
+         || wbbefore == unicode_Word_Break_MidNumLet)
+     && wbafter == unicode_Word_Break_ALetter)
     return 0;
   /* WB8 */
-  if(before == unicode_Word_Break_Numeric
-     && after == unicode_Word_Break_Numeric)
+  if(wbbefore == unicode_Word_Break_Numeric
+     && wbafter == unicode_Word_Break_Numeric)
     return 0;
   /* WB9 */
-  if(before == unicode_Word_Break_ALetter
-     && after == unicode_Word_Break_Numeric)
+  if(wbbefore == unicode_Word_Break_ALetter
+     && wbafter == unicode_Word_Break_Numeric)
     return 0;
   /* WB10 */
-  if(before == unicode_Word_Break_Numeric
-     && after == unicode_Word_Break_ALetter)
+  if(wbbefore == unicode_Word_Break_Numeric
+     && wbafter == unicode_Word_Break_ALetter)
     return 0;
    /* WB11 */
-  if(twobefore == unicode_Word_Break_Numeric
-     && before == unicode_Word_Break_MidNum
-     && after == unicode_Word_Break_Numeric)
+  if(wbtwobefore == unicode_Word_Break_Numeric
+     && (wbbefore == unicode_Word_Break_MidNum
+         || wbbefore == unicode_Word_Break_MidNumLet)
+     && wbafter == unicode_Word_Break_Numeric)
     return 0;
   /* WB12 */
-  if(before == unicode_Word_Break_Numeric
-     && after == unicode_Word_Break_MidNum
-     && twoafter == unicode_Word_Break_Numeric)
+  if(wbbefore == unicode_Word_Break_Numeric
+     && (wbafter == unicode_Word_Break_MidNum
+         || wbafter == unicode_Word_Break_MidNumLet)
+     && wbtwoafter == unicode_Word_Break_Numeric)
     return 0;
   /* WB13 */
-  if(before == unicode_Word_Break_Katakana
-     && after == unicode_Word_Break_Katakana)
+  if(wbbefore == unicode_Word_Break_Katakana
+     && wbafter == unicode_Word_Break_Katakana)
     return 0;
   /* WB13a */
-  if((before == unicode_Word_Break_ALetter
-      || before == unicode_Word_Break_Numeric
-      || before == unicode_Word_Break_Katakana
-      || before == unicode_Word_Break_ExtendNumLet)
-     && after == unicode_Word_Break_ExtendNumLet)
+  if((wbbefore == unicode_Word_Break_ALetter
+      || wbbefore == unicode_Word_Break_Numeric
+      || wbbefore == unicode_Word_Break_Katakana
+      || wbbefore == unicode_Word_Break_ExtendNumLet)
+     && wbafter == unicode_Word_Break_ExtendNumLet)
     return 0;
   /* WB13b */
-  if(before == unicode_Word_Break_ExtendNumLet
-     && (after == unicode_Word_Break_ALetter
-         || after == unicode_Word_Break_Numeric
-         || after == unicode_Word_Break_Katakana))
+  if(wbbefore == unicode_Word_Break_ExtendNumLet
+     && (wbafter == unicode_Word_Break_ALetter
+         || wbafter == unicode_Word_Break_Numeric
+         || wbafter == unicode_Word_Break_Katakana))
     return 0;
   /* WB14 */
   return 1;
@@ -1358,8 +1381,10 @@ uint32_t **utf32_word_split(const uint32_t *s, size_t ns, size_t *nwp,
       }
       /* If it's a word add it to the list of results */
       if(isword) {
-        w = xcalloc(b2 - b1 + 1, sizeof(uint32_t));
-        memcpy(w, it->s + b1, (b2 - b1) * sizeof (uint32_t));
+        const size_t len = b2 - b1;
+        w = xcalloc_noptr(len + 1, sizeof(uint32_t));
+        memcpy(w, it->s + b1, len * sizeof (uint32_t));
+        w[len] = 0;
         vector32_append(v32, w);
       }
     }
index b22b5bc..87b90f5 100644 (file)
 #define GBLV unicode_Grapheme_Break_LV
 #define GBLVT unicode_Grapheme_Break_LVT
 #define GBOther unicode_Grapheme_Break_Other
+#define GBPrepend unicode_Grapheme_Break_Prepend
+#define GBSpacingMark unicode_Grapheme_Break_SpacingMark
 #define GBT unicode_Grapheme_Break_T
 #define GBV unicode_Grapheme_Break_V
 #define WBALetter unicode_Word_Break_ALetter
+#define WBCR unicode_Word_Break_CR
 #define WBExtend unicode_Word_Break_Extend
 #define WBExtendNumLet unicode_Word_Break_ExtendNumLet
 #define WBFormat unicode_Word_Break_Format
 #define WBKatakana unicode_Word_Break_Katakana
+#define WBLF unicode_Word_Break_LF
 #define WBMidLetter unicode_Word_Break_MidLetter
 #define WBMidNum unicode_Word_Break_MidNum
+#define WBMidNumLet unicode_Word_Break_MidNumLet
+#define WBNewline unicode_Word_Break_Newline
 #define WBNumeric unicode_Word_Break_Numeric
 #define WBOther unicode_Word_Break_Other
 #define SBATerm unicode_Sentence_Break_ATerm
+#define SBCR unicode_Sentence_Break_CR
 #define SBClose unicode_Sentence_Break_Close
 #define SBExtend unicode_Sentence_Break_Extend
 #define SBFormat unicode_Sentence_Break_Format
+#define SBLF unicode_Sentence_Break_LF
 #define SBLower unicode_Sentence_Break_Lower
 #define SBNumeric unicode_Sentence_Break_Numeric
 #define SBOLetter unicode_Sentence_Break_OLetter
 #define SBOther unicode_Sentence_Break_Other
+#define SBSContinue unicode_Sentence_Break_SContinue
 #define SBSTerm unicode_Sentence_Break_STerm
 #define SBSep unicode_Sentence_Break_Sep
 #define SBSp unicode_Sentence_Break_Sp
@@ -79,29 +88,38 @@ const char *const unicode_Grapheme_Break_names[] = {
   "LV",
   "LVT",
   "Other",
+  "Prepend",
+  "SpacingMark",
   "T",
   "V"
 };
 const char *const unicode_Word_Break_names[] = {
   "ALetter",
+  "CR",
   "Extend",
   "ExtendNumLet",
   "Format",
   "Katakana",
+  "LF",
   "MidLetter",
   "MidNum",
+  "MidNumLet",
+  "Newline",
   "Numeric",
   "Other"
 };
 const char *const unicode_Sentence_Break_names[] = {
   "ATerm",
+  "CR",
   "Close",
   "Extend",
   "Format",
+  "LF",
   "Lower",
   "Numeric",
   "OLetter",
   "Other",
+  "SContinue",
   "STerm",
   "Sep",
   "Sp",
@@ -2237,2670 +2255,2767 @@ dd2126[]={50,57,26085,0},
 dd2127[]={51,48,26085,0},
 dd2128[]={51,49,26085,0},
 dd2129[]={103,97,108,0},
-dd2130[]={35912,0},
-dd2131[]={26356,0},
-dd2132[]={36040,0},
-dd2133[]={28369,0},
-dd2134[]={20018,0},
-dd2135[]={21477,0},
-dd2136[]={22865,0},
-dd2137[]={21895,0},
-dd2138[]={22856,0},
-dd2139[]={25078,0},
-dd2140[]={30313,0},
-dd2141[]={32645,0},
-dd2142[]={34367,0},
-dd2143[]={34746,0},
-dd2144[]={35064,0},
-dd2145[]={37007,0},
-dd2146[]={27138,0},
-dd2147[]={27931,0},
-dd2148[]={28889,0},
-dd2149[]={29662,0},
-dd2150[]={33853,0},
-dd2151[]={37226,0},
-dd2152[]={39409,0},
-dd2153[]={20098,0},
-dd2154[]={21365,0},
-dd2155[]={27396,0},
-dd2156[]={29211,0},
-dd2157[]={34349,0},
-dd2158[]={40478,0},
-dd2159[]={23888,0},
-dd2160[]={28651,0},
-dd2161[]={34253,0},
-dd2162[]={35172,0},
-dd2163[]={25289,0},
-dd2164[]={33240,0},
-dd2165[]={34847,0},
-dd2166[]={24266,0},
-dd2167[]={26391,0},
-dd2168[]={28010,0},
-dd2169[]={29436,0},
-dd2170[]={37070,0},
-dd2171[]={20358,0},
-dd2172[]={20919,0},
-dd2173[]={21214,0},
-dd2174[]={25796,0},
-dd2175[]={27347,0},
-dd2176[]={29200,0},
-dd2177[]={30439,0},
-dd2178[]={34310,0},
-dd2179[]={34396,0},
-dd2180[]={36335,0},
-dd2181[]={38706,0},
-dd2182[]={39791,0},
-dd2183[]={40442,0},
-dd2184[]={30860,0},
-dd2185[]={31103,0},
-dd2186[]={32160,0},
-dd2187[]={33737,0},
-dd2188[]={37636,0},
-dd2189[]={35542,0},
-dd2190[]={22751,0},
-dd2191[]={24324,0},
-dd2192[]={31840,0},
-dd2193[]={32894,0},
-dd2194[]={29282,0},
-dd2195[]={30922,0},
-dd2196[]={36034,0},
-dd2197[]={38647,0},
-dd2198[]={22744,0},
-dd2199[]={23650,0},
-dd2200[]={27155,0},
-dd2201[]={28122,0},
-dd2202[]={28431,0},
-dd2203[]={32047,0},
-dd2204[]={32311,0},
-dd2205[]={38475,0},
-dd2206[]={21202,0},
-dd2207[]={32907,0},
-dd2208[]={20956,0},
-dd2209[]={20940,0},
-dd2210[]={31260,0},
-dd2211[]={32190,0},
-dd2212[]={33777,0},
-dd2213[]={38517,0},
-dd2214[]={35712,0},
-dd2215[]={25295,0},
-dd2216[]={35582,0},
-dd2217[]={20025,0},
-dd2218[]={23527,0},
-dd2219[]={24594,0},
-dd2220[]={29575,0},
-dd2221[]={30064,0},
-dd2222[]={21271,0},
-dd2223[]={30971,0},
-dd2224[]={20415,0},
-dd2225[]={24489,0},
-dd2226[]={19981,0},
-dd2227[]={27852,0},
-dd2228[]={25976,0},
-dd2229[]={32034,0},
-dd2230[]={21443,0},
-dd2231[]={22622,0},
-dd2232[]={30465,0},
-dd2233[]={33865,0},
-dd2234[]={35498,0},
-dd2235[]={27578,0},
-dd2236[]={27784,0},
-dd2237[]={25342,0},
-dd2238[]={33509,0},
-dd2239[]={25504,0},
-dd2240[]={30053,0},
-dd2241[]={20142,0},
-dd2242[]={20841,0},
-dd2243[]={20937,0},
-dd2244[]={26753,0},
-dd2245[]={31975,0},
-dd2246[]={33391,0},
-dd2247[]={35538,0},
-dd2248[]={37327,0},
-dd2249[]={21237,0},
-dd2250[]={21570,0},
-dd2251[]={24300,0},
-dd2252[]={26053,0},
-dd2253[]={28670,0},
-dd2254[]={31018,0},
-dd2255[]={38317,0},
-dd2256[]={39530,0},
-dd2257[]={40599,0},
-dd2258[]={40654,0},
-dd2259[]={26310,0},
-dd2260[]={27511,0},
-dd2261[]={36706,0},
-dd2262[]={24180,0},
-dd2263[]={24976,0},
-dd2264[]={25088,0},
-dd2265[]={25754,0},
-dd2266[]={28451,0},
-dd2267[]={29001,0},
-dd2268[]={29833,0},
-dd2269[]={31178,0},
-dd2270[]={32244,0},
-dd2271[]={32879,0},
-dd2272[]={36646,0},
-dd2273[]={34030,0},
-dd2274[]={36899,0},
-dd2275[]={37706,0},
-dd2276[]={21015,0},
-dd2277[]={21155,0},
-dd2278[]={21693,0},
-dd2279[]={28872,0},
-dd2280[]={35010,0},
-dd2281[]={24265,0},
-dd2282[]={24565,0},
-dd2283[]={25467,0},
-dd2284[]={27566,0},
-dd2285[]={31806,0},
-dd2286[]={29557,0},
-dd2287[]={20196,0},
-dd2288[]={22265,0},
-dd2289[]={23994,0},
-dd2290[]={24604,0},
-dd2291[]={29618,0},
-dd2292[]={29801,0},
-dd2293[]={32666,0},
-dd2294[]={32838,0},
-dd2295[]={37428,0},
-dd2296[]={38646,0},
-dd2297[]={38728,0},
-dd2298[]={38936,0},
-dd2299[]={20363,0},
-dd2300[]={31150,0},
-dd2301[]={37300,0},
-dd2302[]={38584,0},
-dd2303[]={24801,0},
-dd2304[]={20102,0},
-dd2305[]={20698,0},
-dd2306[]={23534,0},
-dd2307[]={23615,0},
-dd2308[]={26009,0},
-dd2309[]={29134,0},
-dd2310[]={30274,0},
-dd2311[]={34044,0},
-dd2312[]={36988,0},
-dd2313[]={26248,0},
-dd2314[]={38446,0},
-dd2315[]={21129,0},
-dd2316[]={26491,0},
-dd2317[]={26611,0},
-dd2318[]={27969,0},
-dd2319[]={28316,0},
-dd2320[]={29705,0},
-dd2321[]={30041,0},
-dd2322[]={30827,0},
-dd2323[]={32016,0},
-dd2324[]={39006,0},
-dd2325[]={25134,0},
-dd2326[]={38520,0},
-dd2327[]={20523,0},
-dd2328[]={23833,0},
-dd2329[]={28138,0},
-dd2330[]={36650,0},
-dd2331[]={24459,0},
-dd2332[]={24900,0},
-dd2333[]={26647,0},
-dd2334[]={38534,0},
-dd2335[]={21033,0},
-dd2336[]={21519,0},
-dd2337[]={23653,0},
-dd2338[]={26131,0},
-dd2339[]={26446,0},
-dd2340[]={26792,0},
-dd2341[]={27877,0},
-dd2342[]={29702,0},
-dd2343[]={30178,0},
-dd2344[]={32633,0},
-dd2345[]={35023,0},
-dd2346[]={35041,0},
-dd2347[]={38626,0},
-dd2348[]={21311,0},
-dd2349[]={28346,0},
-dd2350[]={21533,0},
-dd2351[]={29136,0},
-dd2352[]={29848,0},
-dd2353[]={34298,0},
-dd2354[]={38563,0},
-dd2355[]={40023,0},
-dd2356[]={40607,0},
-dd2357[]={26519,0},
-dd2358[]={28107,0},
-dd2359[]={33256,0},
-dd2360[]={31520,0},
-dd2361[]={31890,0},
-dd2362[]={29376,0},
-dd2363[]={28825,0},
-dd2364[]={35672,0},
-dd2365[]={20160,0},
-dd2366[]={33590,0},
-dd2367[]={21050,0},
-dd2368[]={20999,0},
-dd2369[]={24230,0},
-dd2370[]={25299,0},
-dd2371[]={31958,0},
-dd2372[]={23429,0},
-dd2373[]={27934,0},
-dd2374[]={26292,0},
-dd2375[]={36667,0},
-dd2376[]={38477,0},
-dd2377[]={24275,0},
-dd2378[]={20800,0},
-dd2379[]={21952,0},
-dd2380[]={22618,0},
-dd2381[]={26228,0},
-dd2382[]={20958,0},
-dd2383[]={29482,0},
-dd2384[]={30410,0},
-dd2385[]={31036,0},
-dd2386[]={31070,0},
-dd2387[]={31077,0},
-dd2388[]={31119,0},
-dd2389[]={38742,0},
-dd2390[]={31934,0},
-dd2391[]={34322,0},
-dd2392[]={35576,0},
-dd2393[]={36920,0},
-dd2394[]={37117,0},
-dd2395[]={39151,0},
-dd2396[]={39164,0},
-dd2397[]={39208,0},
-dd2398[]={40372,0},
-dd2399[]={20398,0},
-dd2400[]={20711,0},
-dd2401[]={20813,0},
-dd2402[]={21193,0},
-dd2403[]={21220,0},
-dd2404[]={21329,0},
-dd2405[]={21917,0},
-dd2406[]={22022,0},
-dd2407[]={22120,0},
-dd2408[]={22592,0},
-dd2409[]={22696,0},
-dd2410[]={23652,0},
-dd2411[]={24724,0},
-dd2412[]={24936,0},
-dd2413[]={24974,0},
-dd2414[]={25074,0},
-dd2415[]={25935,0},
-dd2416[]={26082,0},
-dd2417[]={26257,0},
-dd2418[]={26757,0},
-dd2419[]={28023,0},
-dd2420[]={28186,0},
-dd2421[]={28450,0},
-dd2422[]={29038,0},
-dd2423[]={29227,0},
-dd2424[]={29730,0},
-dd2425[]={30865,0},
-dd2426[]={31049,0},
-dd2427[]={31048,0},
-dd2428[]={31056,0},
-dd2429[]={31062,0},
-dd2430[]={31117,0},
-dd2431[]={31118,0},
-dd2432[]={31296,0},
-dd2433[]={31361,0},
-dd2434[]={31680,0},
-dd2435[]={32265,0},
-dd2436[]={32321,0},
-dd2437[]={32626,0},
-dd2438[]={32773,0},
-dd2439[]={33261,0},
-dd2440[]={33401,0},
-dd2441[]={33879,0},
-dd2442[]={35088,0},
-dd2443[]={35222,0},
-dd2444[]={35585,0},
-dd2445[]={35641,0},
-dd2446[]={36051,0},
-dd2447[]={36104,0},
-dd2448[]={36790,0},
-dd2449[]={38627,0},
-dd2450[]={38911,0},
-dd2451[]={38971,0},
-dd2452[]={20006,0},
-dd2453[]={20917,0},
-dd2454[]={20840,0},
-dd2455[]={20352,0},
-dd2456[]={20805,0},
-dd2457[]={20864,0},
-dd2458[]={21191,0},
-dd2459[]={21242,0},
-dd2460[]={21845,0},
-dd2461[]={21913,0},
-dd2462[]={21986,0},
-dd2463[]={22707,0},
-dd2464[]={22852,0},
-dd2465[]={22868,0},
-dd2466[]={23138,0},
-dd2467[]={23336,0},
-dd2468[]={24274,0},
-dd2469[]={24281,0},
-dd2470[]={24425,0},
-dd2471[]={24493,0},
-dd2472[]={24792,0},
-dd2473[]={24910,0},
-dd2474[]={24840,0},
-dd2475[]={24928,0},
-dd2476[]={25140,0},
-dd2477[]={25540,0},
-dd2478[]={25628,0},
-dd2479[]={25682,0},
-dd2480[]={25942,0},
-dd2481[]={26395,0},
-dd2482[]={26454,0},
-dd2483[]={28379,0},
-dd2484[]={28363,0},
-dd2485[]={28702,0},
-dd2486[]={30631,0},
-dd2487[]={29237,0},
-dd2488[]={29359,0},
-dd2489[]={29809,0},
-dd2490[]={29958,0},
-dd2491[]={30011,0},
-dd2492[]={30237,0},
-dd2493[]={30239,0},
-dd2494[]={30427,0},
-dd2495[]={30452,0},
-dd2496[]={30538,0},
-dd2497[]={30528,0},
-dd2498[]={30924,0},
-dd2499[]={31409,0},
-dd2500[]={31867,0},
-dd2501[]={32091,0},
-dd2502[]={32574,0},
-dd2503[]={33618,0},
-dd2504[]={33775,0},
-dd2505[]={34681,0},
-dd2506[]={35137,0},
-dd2507[]={35206,0},
-dd2508[]={35519,0},
-dd2509[]={35531,0},
-dd2510[]={35565,0},
-dd2511[]={35722,0},
-dd2512[]={36664,0},
-dd2513[]={36978,0},
-dd2514[]={37273,0},
-dd2515[]={37494,0},
-dd2516[]={38524,0},
-dd2517[]={38875,0},
-dd2518[]={38923,0},
-dd2519[]={39698,0},
-dd2520[]={141386,0},
-dd2521[]={141380,0},
-dd2522[]={144341,0},
-dd2523[]={15261,0},
-dd2524[]={16408,0},
-dd2525[]={16441,0},
-dd2526[]={152137,0},
-dd2527[]={154832,0},
-dd2528[]={163539,0},
-dd2529[]={40771,0},
-dd2530[]={40846,0},
-dd2531[]={102,102,0},
-dd2532[]={102,105,0},
-dd2533[]={102,108,0},
-dd2534[]={102,102,105,0},
-dd2535[]={102,102,108,0},
-dd2536[]={383,116,0},
-dd2537[]={115,116,0},
-dd2538[]={1396,1398,0},
-dd2539[]={1396,1381,0},
-dd2540[]={1396,1387,0},
-dd2541[]={1406,1398,0},
-dd2542[]={1396,1389,0},
-dd2543[]={1497,1460,0},
-dd2544[]={1522,1463,0},
-dd2545[]={1506,0},
-dd2546[]={1492,0},
-dd2547[]={1499,0},
-dd2548[]={1500,0},
-dd2549[]={1501,0},
-dd2550[]={1512,0},
-dd2551[]={1514,0},
-dd2552[]={1513,1473,0},
-dd2553[]={1513,1474,0},
-dd2554[]={64329,1473,0},
-dd2555[]={64329,1474,0},
-dd2556[]={1488,1463,0},
-dd2557[]={1488,1464,0},
-dd2558[]={1488,1468,0},
-dd2559[]={1489,1468,0},
-dd2560[]={1490,1468,0},
-dd2561[]={1491,1468,0},
-dd2562[]={1492,1468,0},
-dd2563[]={1493,1468,0},
-dd2564[]={1494,1468,0},
-dd2565[]={1496,1468,0},
-dd2566[]={1497,1468,0},
-dd2567[]={1498,1468,0},
-dd2568[]={1499,1468,0},
-dd2569[]={1500,1468,0},
-dd2570[]={1502,1468,0},
-dd2571[]={1504,1468,0},
-dd2572[]={1505,1468,0},
-dd2573[]={1507,1468,0},
-dd2574[]={1508,1468,0},
-dd2575[]={1510,1468,0},
-dd2576[]={1511,1468,0},
-dd2577[]={1512,1468,0},
-dd2578[]={1513,1468,0},
-dd2579[]={1514,1468,0},
-dd2580[]={1493,1465,0},
-dd2581[]={1489,1471,0},
-dd2582[]={1499,1471,0},
-dd2583[]={1508,1471,0},
-dd2584[]={1488,1500,0},
-dd2585[]={1649,0},
-dd2586[]={1659,0},
-dd2587[]={1662,0},
-dd2588[]={1664,0},
-dd2589[]={1658,0},
-dd2590[]={1663,0},
-dd2591[]={1657,0},
-dd2592[]={1700,0},
-dd2593[]={1702,0},
-dd2594[]={1668,0},
-dd2595[]={1667,0},
-dd2596[]={1670,0},
-dd2597[]={1671,0},
-dd2598[]={1677,0},
-dd2599[]={1676,0},
-dd2600[]={1678,0},
-dd2601[]={1672,0},
-dd2602[]={1688,0},
-dd2603[]={1681,0},
-dd2604[]={1705,0},
-dd2605[]={1711,0},
-dd2606[]={1715,0},
-dd2607[]={1713,0},
-dd2608[]={1722,0},
-dd2609[]={1723,0},
-dd2610[]={1728,0},
-dd2611[]={1729,0},
-dd2612[]={1726,0},
-dd2613[]={1746,0},
-dd2614[]={1747,0},
-dd2615[]={1709,0},
-dd2616[]={1735,0},
-dd2617[]={1734,0},
-dd2618[]={1736,0},
-dd2619[]={1655,0},
-dd2620[]={1739,0},
-dd2621[]={1733,0},
-dd2622[]={1737,0},
-dd2623[]={1744,0},
-dd2624[]={1609,0},
-dd2625[]={1574,1575,0},
-dd2626[]={1574,1749,0},
-dd2627[]={1574,1608,0},
-dd2628[]={1574,1735,0},
-dd2629[]={1574,1734,0},
-dd2630[]={1574,1736,0},
-dd2631[]={1574,1744,0},
-dd2632[]={1574,1609,0},
-dd2633[]={1740,0},
-dd2634[]={1574,1580,0},
-dd2635[]={1574,1581,0},
-dd2636[]={1574,1605,0},
-dd2637[]={1574,1610,0},
-dd2638[]={1576,1580,0},
-dd2639[]={1576,1581,0},
-dd2640[]={1576,1582,0},
-dd2641[]={1576,1605,0},
-dd2642[]={1576,1609,0},
-dd2643[]={1576,1610,0},
-dd2644[]={1578,1580,0},
-dd2645[]={1578,1581,0},
-dd2646[]={1578,1582,0},
-dd2647[]={1578,1605,0},
-dd2648[]={1578,1609,0},
-dd2649[]={1578,1610,0},
-dd2650[]={1579,1580,0},
-dd2651[]={1579,1605,0},
-dd2652[]={1579,1609,0},
-dd2653[]={1579,1610,0},
-dd2654[]={1580,1581,0},
-dd2655[]={1580,1605,0},
-dd2656[]={1581,1580,0},
-dd2657[]={1581,1605,0},
-dd2658[]={1582,1580,0},
-dd2659[]={1582,1581,0},
-dd2660[]={1582,1605,0},
-dd2661[]={1587,1580,0},
-dd2662[]={1587,1581,0},
-dd2663[]={1587,1582,0},
-dd2664[]={1587,1605,0},
-dd2665[]={1589,1581,0},
-dd2666[]={1589,1605,0},
-dd2667[]={1590,1580,0},
-dd2668[]={1590,1581,0},
-dd2669[]={1590,1582,0},
-dd2670[]={1590,1605,0},
-dd2671[]={1591,1581,0},
-dd2672[]={1591,1605,0},
-dd2673[]={1592,1605,0},
-dd2674[]={1593,1580,0},
-dd2675[]={1593,1605,0},
-dd2676[]={1594,1580,0},
-dd2677[]={1594,1605,0},
-dd2678[]={1601,1580,0},
-dd2679[]={1601,1581,0},
-dd2680[]={1601,1582,0},
-dd2681[]={1601,1605,0},
-dd2682[]={1601,1609,0},
-dd2683[]={1601,1610,0},
-dd2684[]={1602,1581,0},
-dd2685[]={1602,1605,0},
-dd2686[]={1602,1609,0},
-dd2687[]={1602,1610,0},
-dd2688[]={1603,1575,0},
-dd2689[]={1603,1580,0},
-dd2690[]={1603,1581,0},
-dd2691[]={1603,1582,0},
-dd2692[]={1603,1604,0},
-dd2693[]={1603,1605,0},
-dd2694[]={1603,1609,0},
-dd2695[]={1603,1610,0},
-dd2696[]={1604,1580,0},
-dd2697[]={1604,1581,0},
-dd2698[]={1604,1582,0},
-dd2699[]={1604,1605,0},
-dd2700[]={1604,1609,0},
-dd2701[]={1604,1610,0},
-dd2702[]={1605,1580,0},
-dd2703[]={1605,1581,0},
-dd2704[]={1605,1582,0},
-dd2705[]={1605,1605,0},
-dd2706[]={1605,1609,0},
-dd2707[]={1605,1610,0},
-dd2708[]={1606,1580,0},
-dd2709[]={1606,1581,0},
-dd2710[]={1606,1582,0},
-dd2711[]={1606,1605,0},
-dd2712[]={1606,1609,0},
-dd2713[]={1606,1610,0},
-dd2714[]={1607,1580,0},
-dd2715[]={1607,1605,0},
-dd2716[]={1607,1609,0},
-dd2717[]={1607,1610,0},
-dd2718[]={1610,1580,0},
-dd2719[]={1610,1581,0},
-dd2720[]={1610,1582,0},
-dd2721[]={1610,1605,0},
-dd2722[]={1610,1609,0},
-dd2723[]={1610,1610,0},
-dd2724[]={1584,1648,0},
-dd2725[]={1585,1648,0},
-dd2726[]={1609,1648,0},
-dd2727[]={32,1612,1617,0},
-dd2728[]={32,1613,1617,0},
-dd2729[]={32,1614,1617,0},
-dd2730[]={32,1615,1617,0},
-dd2731[]={32,1616,1617,0},
-dd2732[]={32,1617,1648,0},
-dd2733[]={1574,1585,0},
-dd2734[]={1574,1586,0},
-dd2735[]={1574,1606,0},
-dd2736[]={1576,1585,0},
-dd2737[]={1576,1586,0},
-dd2738[]={1576,1606,0},
-dd2739[]={1578,1585,0},
-dd2740[]={1578,1586,0},
-dd2741[]={1578,1606,0},
-dd2742[]={1579,1585,0},
-dd2743[]={1579,1586,0},
-dd2744[]={1579,1606,0},
-dd2745[]={1605,1575,0},
-dd2746[]={1606,1585,0},
-dd2747[]={1606,1586,0},
-dd2748[]={1606,1606,0},
-dd2749[]={1610,1585,0},
-dd2750[]={1610,1586,0},
-dd2751[]={1610,1606,0},
-dd2752[]={1574,1582,0},
-dd2753[]={1574,1607,0},
-dd2754[]={1576,1607,0},
-dd2755[]={1578,1607,0},
-dd2756[]={1589,1582,0},
-dd2757[]={1604,1607,0},
-dd2758[]={1606,1607,0},
-dd2759[]={1607,1648,0},
-dd2760[]={1610,1607,0},
-dd2761[]={1579,1607,0},
-dd2762[]={1587,1607,0},
-dd2763[]={1588,1605,0},
-dd2764[]={1588,1607,0},
-dd2765[]={1600,1614,1617,0},
-dd2766[]={1600,1615,1617,0},
-dd2767[]={1600,1616,1617,0},
-dd2768[]={1591,1609,0},
-dd2769[]={1591,1610,0},
-dd2770[]={1593,1609,0},
-dd2771[]={1593,1610,0},
-dd2772[]={1594,1609,0},
-dd2773[]={1594,1610,0},
-dd2774[]={1587,1609,0},
-dd2775[]={1587,1610,0},
-dd2776[]={1588,1609,0},
-dd2777[]={1588,1610,0},
-dd2778[]={1581,1609,0},
-dd2779[]={1581,1610,0},
-dd2780[]={1580,1609,0},
-dd2781[]={1580,1610,0},
-dd2782[]={1582,1609,0},
-dd2783[]={1582,1610,0},
-dd2784[]={1589,1609,0},
-dd2785[]={1589,1610,0},
-dd2786[]={1590,1609,0},
-dd2787[]={1590,1610,0},
-dd2788[]={1588,1580,0},
-dd2789[]={1588,1581,0},
-dd2790[]={1588,1582,0},
-dd2791[]={1588,1585,0},
-dd2792[]={1587,1585,0},
-dd2793[]={1589,1585,0},
-dd2794[]={1590,1585,0},
-dd2795[]={1575,1611,0},
-dd2796[]={1578,1580,1605,0},
-dd2797[]={1578,1581,1580,0},
-dd2798[]={1578,1581,1605,0},
-dd2799[]={1578,1582,1605,0},
-dd2800[]={1578,1605,1580,0},
-dd2801[]={1578,1605,1581,0},
-dd2802[]={1578,1605,1582,0},
-dd2803[]={1580,1605,1581,0},
-dd2804[]={1581,1605,1610,0},
-dd2805[]={1581,1605,1609,0},
-dd2806[]={1587,1581,1580,0},
-dd2807[]={1587,1580,1581,0},
-dd2808[]={1587,1580,1609,0},
-dd2809[]={1587,1605,1581,0},
-dd2810[]={1587,1605,1580,0},
-dd2811[]={1587,1605,1605,0},
-dd2812[]={1589,1581,1581,0},
-dd2813[]={1589,1605,1605,0},
-dd2814[]={1588,1581,1605,0},
-dd2815[]={1588,1580,1610,0},
-dd2816[]={1588,1605,1582,0},
-dd2817[]={1588,1605,1605,0},
-dd2818[]={1590,1581,1609,0},
-dd2819[]={1590,1582,1605,0},
-dd2820[]={1591,1605,1581,0},
-dd2821[]={1591,1605,1605,0},
-dd2822[]={1591,1605,1610,0},
-dd2823[]={1593,1580,1605,0},
-dd2824[]={1593,1605,1605,0},
-dd2825[]={1593,1605,1609,0},
-dd2826[]={1594,1605,1605,0},
-dd2827[]={1594,1605,1610,0},
-dd2828[]={1594,1605,1609,0},
-dd2829[]={1601,1582,1605,0},
-dd2830[]={1602,1605,1581,0},
-dd2831[]={1602,1605,1605,0},
-dd2832[]={1604,1581,1605,0},
-dd2833[]={1604,1581,1610,0},
-dd2834[]={1604,1581,1609,0},
-dd2835[]={1604,1580,1580,0},
-dd2836[]={1604,1582,1605,0},
-dd2837[]={1604,1605,1581,0},
-dd2838[]={1605,1581,1580,0},
-dd2839[]={1605,1581,1605,0},
-dd2840[]={1605,1581,1610,0},
-dd2841[]={1605,1580,1581,0},
-dd2842[]={1605,1580,1605,0},
-dd2843[]={1605,1582,1580,0},
-dd2844[]={1605,1582,1605,0},
-dd2845[]={1605,1580,1582,0},
-dd2846[]={1607,1605,1580,0},
-dd2847[]={1607,1605,1605,0},
-dd2848[]={1606,1581,1605,0},
-dd2849[]={1606,1581,1609,0},
-dd2850[]={1606,1580,1605,0},
-dd2851[]={1606,1580,1609,0},
-dd2852[]={1606,1605,1610,0},
-dd2853[]={1606,1605,1609,0},
-dd2854[]={1610,1605,1605,0},
-dd2855[]={1576,1582,1610,0},
-dd2856[]={1578,1580,1610,0},
-dd2857[]={1578,1580,1609,0},
-dd2858[]={1578,1582,1610,0},
-dd2859[]={1578,1582,1609,0},
-dd2860[]={1578,1605,1610,0},
-dd2861[]={1578,1605,1609,0},
-dd2862[]={1580,1605,1610,0},
-dd2863[]={1580,1581,1609,0},
-dd2864[]={1580,1605,1609,0},
-dd2865[]={1587,1582,1609,0},
-dd2866[]={1589,1581,1610,0},
-dd2867[]={1588,1581,1610,0},
-dd2868[]={1590,1581,1610,0},
-dd2869[]={1604,1580,1610,0},
-dd2870[]={1604,1605,1610,0},
-dd2871[]={1610,1581,1610,0},
-dd2872[]={1610,1580,1610,0},
-dd2873[]={1610,1605,1610,0},
-dd2874[]={1605,1605,1610,0},
-dd2875[]={1602,1605,1610,0},
-dd2876[]={1606,1581,1610,0},
-dd2877[]={1593,1605,1610,0},
-dd2878[]={1603,1605,1610,0},
-dd2879[]={1606,1580,1581,0},
-dd2880[]={1605,1582,1610,0},
-dd2881[]={1604,1580,1605,0},
-dd2882[]={1603,1605,1605,0},
-dd2883[]={1580,1581,1610,0},
-dd2884[]={1581,1580,1610,0},
-dd2885[]={1605,1580,1610,0},
-dd2886[]={1601,1605,1610,0},
-dd2887[]={1576,1581,1610,0},
-dd2888[]={1587,1582,1610,0},
-dd2889[]={1606,1580,1610,0},
-dd2890[]={1589,1604,1746,0},
-dd2891[]={1602,1604,1746,0},
-dd2892[]={1575,1604,1604,1607,0},
-dd2893[]={1575,1603,1576,1585,0},
-dd2894[]={1605,1581,1605,1583,0},
-dd2895[]={1589,1604,1593,1605,0},
-dd2896[]={1585,1587,1608,1604,0},
-dd2897[]={1593,1604,1610,1607,0},
-dd2898[]={1608,1587,1604,1605,0},
-dd2899[]={1589,1604,1609,0},
-dd2900[]={1589,1604,1609,32,1575,1604,1604,1607,32,1593,1604,1610,1607,32,1608,1587,1604,1605,0},
-dd2901[]={1580,1604,32,1580,1604,1575,1604,1607,0},
-dd2902[]={1585,1740,1575,1604,0},
-dd2903[]={44,0},
-dd2904[]={12289,0},
-dd2905[]={12290,0},
-dd2906[]={58,0},
-dd2907[]={33,0},
-dd2908[]={63,0},
-dd2909[]={12310,0},
-dd2910[]={12311,0},
-dd2911[]={8230,0},
-dd2912[]={8229,0},
-dd2913[]={8212,0},
-dd2914[]={8211,0},
-dd2915[]={95,0},
-dd2916[]={123,0},
-dd2917[]={125,0},
-dd2918[]={12308,0},
-dd2919[]={12309,0},
-dd2920[]={12304,0},
-dd2921[]={12305,0},
-dd2922[]={12298,0},
-dd2923[]={12299,0},
-dd2924[]={12300,0},
-dd2925[]={12301,0},
-dd2926[]={12302,0},
-dd2927[]={12303,0},
-dd2928[]={91,0},
-dd2929[]={93,0},
-dd2930[]={8254,0},
-dd2931[]={35,0},
-dd2932[]={38,0},
-dd2933[]={42,0},
-dd2934[]={45,0},
-dd2935[]={60,0},
-dd2936[]={62,0},
-dd2937[]={92,0},
-dd2938[]={36,0},
-dd2939[]={37,0},
-dd2940[]={64,0},
-dd2941[]={32,1611,0},
-dd2942[]={1600,1611,0},
-dd2943[]={32,1612,0},
-dd2944[]={32,1613,0},
-dd2945[]={32,1614,0},
-dd2946[]={1600,1614,0},
-dd2947[]={32,1615,0},
-dd2948[]={1600,1615,0},
-dd2949[]={32,1616,0},
-dd2950[]={1600,1616,0},
-dd2951[]={32,1617,0},
-dd2952[]={1600,1617,0},
-dd2953[]={32,1618,0},
-dd2954[]={1600,1618,0},
-dd2955[]={1569,0},
-dd2956[]={1570,0},
-dd2957[]={1571,0},
-dd2958[]={1572,0},
-dd2959[]={1573,0},
-dd2960[]={1574,0},
-dd2961[]={1575,0},
-dd2962[]={1576,0},
-dd2963[]={1577,0},
-dd2964[]={1578,0},
-dd2965[]={1579,0},
-dd2966[]={1580,0},
-dd2967[]={1581,0},
-dd2968[]={1582,0},
-dd2969[]={1583,0},
-dd2970[]={1584,0},
-dd2971[]={1585,0},
-dd2972[]={1586,0},
-dd2973[]={1587,0},
-dd2974[]={1588,0},
-dd2975[]={1589,0},
-dd2976[]={1590,0},
-dd2977[]={1591,0},
-dd2978[]={1592,0},
-dd2979[]={1593,0},
-dd2980[]={1594,0},
-dd2981[]={1601,0},
-dd2982[]={1602,0},
-dd2983[]={1603,0},
-dd2984[]={1604,0},
-dd2985[]={1605,0},
-dd2986[]={1606,0},
-dd2987[]={1607,0},
-dd2988[]={1608,0},
-dd2989[]={1610,0},
-dd2990[]={1604,1570,0},
-dd2991[]={1604,1571,0},
-dd2992[]={1604,1573,0},
-dd2993[]={1604,1575,0},
-dd2994[]={34,0},
-dd2995[]={39,0},
-dd2996[]={47,0},
-dd2997[]={94,0},
-dd2998[]={124,0},
-dd2999[]={126,0},
-dd3000[]={10629,0},
-dd3001[]={10630,0},
-dd3002[]={12539,0},
-dd3003[]={12449,0},
-dd3004[]={12451,0},
-dd3005[]={12453,0},
-dd3006[]={12455,0},
-dd3007[]={12457,0},
-dd3008[]={12515,0},
-dd3009[]={12517,0},
-dd3010[]={12519,0},
-dd3011[]={12483,0},
-dd3012[]={12540,0},
-dd3013[]={12531,0},
-dd3014[]={12441,0},
-dd3015[]={12442,0},
-dd3016[]={12644,0},
-dd3017[]={12593,0},
-dd3018[]={12594,0},
-dd3019[]={12595,0},
-dd3020[]={12596,0},
-dd3021[]={12597,0},
-dd3022[]={12598,0},
-dd3023[]={12599,0},
-dd3024[]={12600,0},
-dd3025[]={12601,0},
-dd3026[]={12602,0},
-dd3027[]={12603,0},
-dd3028[]={12604,0},
-dd3029[]={12605,0},
-dd3030[]={12606,0},
-dd3031[]={12607,0},
-dd3032[]={12608,0},
-dd3033[]={12609,0},
-dd3034[]={12610,0},
-dd3035[]={12611,0},
-dd3036[]={12612,0},
-dd3037[]={12613,0},
-dd3038[]={12614,0},
-dd3039[]={12615,0},
-dd3040[]={12616,0},
-dd3041[]={12617,0},
-dd3042[]={12618,0},
-dd3043[]={12619,0},
-dd3044[]={12620,0},
-dd3045[]={12621,0},
-dd3046[]={12622,0},
-dd3047[]={12623,0},
-dd3048[]={12624,0},
-dd3049[]={12625,0},
-dd3050[]={12626,0},
-dd3051[]={12627,0},
-dd3052[]={12628,0},
-dd3053[]={12629,0},
-dd3054[]={12630,0},
-dd3055[]={12631,0},
-dd3056[]={12632,0},
-dd3057[]={12633,0},
-dd3058[]={12634,0},
-dd3059[]={12635,0},
-dd3060[]={12636,0},
-dd3061[]={12637,0},
-dd3062[]={12638,0},
-dd3063[]={12639,0},
-dd3064[]={12640,0},
-dd3065[]={12641,0},
-dd3066[]={12642,0},
-dd3067[]={12643,0},
-dd3068[]={162,0},
-dd3069[]={163,0},
-dd3070[]={172,0},
-dd3071[]={175,0},
-dd3072[]={166,0},
-dd3073[]={165,0},
-dd3074[]={8361,0},
-dd3075[]={9474,0},
-dd3076[]={8592,0},
-dd3077[]={8593,0},
-dd3078[]={8594,0},
-dd3079[]={8595,0},
-dd3080[]={9632,0},
-dd3081[]={9675,0},
-dd3082[]={119127,119141,0},
-dd3083[]={119128,119141,0},
-dd3084[]={119135,119150,0},
-dd3085[]={119135,119151,0},
-dd3086[]={119135,119152,0},
-dd3087[]={119135,119153,0},
-dd3088[]={119135,119154,0},
-dd3089[]={119225,119141,0},
-dd3090[]={119226,119141,0},
-dd3091[]={119227,119150,0},
-dd3092[]={119228,119150,0},
-dd3093[]={119227,119151,0},
-dd3094[]={119228,119151,0},
-dd3095[]={305,0},
-dd3096[]={567,0},
-dd3097[]={913,0},
-dd3098[]={914,0},
-dd3099[]={916,0},
-dd3100[]={917,0},
-dd3101[]={918,0},
-dd3102[]={919,0},
-dd3103[]={921,0},
-dd3104[]={922,0},
-dd3105[]={923,0},
-dd3106[]={924,0},
-dd3107[]={925,0},
-dd3108[]={926,0},
-dd3109[]={927,0},
-dd3110[]={929,0},
-dd3111[]={1012,0},
-dd3112[]={932,0},
-dd3113[]={934,0},
-dd3114[]={935,0},
-dd3115[]={936,0},
-dd3116[]={8711,0},
-dd3117[]={945,0},
-dd3118[]={950,0},
-dd3119[]={951,0},
-dd3120[]={955,0},
-dd3121[]={957,0},
-dd3122[]={958,0},
-dd3123[]={959,0},
-dd3124[]={963,0},
-dd3125[]={964,0},
-dd3126[]={965,0},
-dd3127[]={968,0},
-dd3128[]={969,0},
-dd3129[]={8706,0},
-dd3130[]={1013,0},
-dd3131[]={977,0},
-dd3132[]={1008,0},
-dd3133[]={981,0},
-dd3134[]={1009,0},
-dd3135[]={982,0},
-dd3136[]={988,0},
-dd3137[]={989,0},
-dd3138[]={20029,0},
-dd3139[]={20024,0},
-dd3140[]={20033,0},
-dd3141[]={131362,0},
-dd3142[]={20320,0},
-dd3143[]={20411,0},
-dd3144[]={20482,0},
-dd3145[]={20602,0},
-dd3146[]={20633,0},
-dd3147[]={20687,0},
-dd3148[]={13470,0},
-dd3149[]={132666,0},
-dd3150[]={20820,0},
-dd3151[]={20836,0},
-dd3152[]={20855,0},
-dd3153[]={132380,0},
-dd3154[]={13497,0},
-dd3155[]={20839,0},
-dd3156[]={20877,0},
-dd3157[]={132427,0},
-dd3158[]={20887,0},
-dd3159[]={20900,0},
-dd3160[]={20172,0},
-dd3161[]={20908,0},
-dd3162[]={168415,0},
-dd3163[]={20995,0},
-dd3164[]={13535,0},
-dd3165[]={21051,0},
-dd3166[]={21062,0},
-dd3167[]={21106,0},
-dd3168[]={21111,0},
-dd3169[]={13589,0},
-dd3170[]={21253,0},
-dd3171[]={21254,0},
-dd3172[]={21321,0},
-dd3173[]={21338,0},
-dd3174[]={21363,0},
-dd3175[]={21373,0},
-dd3176[]={21375,0},
-dd3177[]={133676,0},
-dd3178[]={28784,0},
-dd3179[]={21450,0},
-dd3180[]={21471,0},
-dd3181[]={133987,0},
-dd3182[]={21483,0},
-dd3183[]={21489,0},
-dd3184[]={21510,0},
-dd3185[]={21662,0},
-dd3186[]={21560,0},
-dd3187[]={21576,0},
-dd3188[]={21608,0},
-dd3189[]={21666,0},
-dd3190[]={21750,0},
-dd3191[]={21776,0},
-dd3192[]={21843,0},
-dd3193[]={21859,0},
-dd3194[]={21892,0},
-dd3195[]={21931,0},
-dd3196[]={21939,0},
-dd3197[]={21954,0},
-dd3198[]={22294,0},
-dd3199[]={22295,0},
-dd3200[]={22097,0},
-dd3201[]={22132,0},
-dd3202[]={22766,0},
-dd3203[]={22478,0},
-dd3204[]={22516,0},
-dd3205[]={22541,0},
-dd3206[]={22411,0},
-dd3207[]={22578,0},
-dd3208[]={22577,0},
-dd3209[]={22700,0},
-dd3210[]={136420,0},
-dd3211[]={22770,0},
-dd3212[]={22775,0},
-dd3213[]={22790,0},
-dd3214[]={22810,0},
-dd3215[]={22818,0},
-dd3216[]={22882,0},
-dd3217[]={136872,0},
-dd3218[]={136938,0},
-dd3219[]={23020,0},
-dd3220[]={23067,0},
-dd3221[]={23079,0},
-dd3222[]={23000,0},
-dd3223[]={23142,0},
-dd3224[]={14062,0},
-dd3225[]={14076,0},
-dd3226[]={23304,0},
-dd3227[]={23358,0},
-dd3228[]={137672,0},
-dd3229[]={23491,0},
-dd3230[]={23512,0},
-dd3231[]={23539,0},
-dd3232[]={138008,0},
-dd3233[]={23551,0},
-dd3234[]={23558,0},
-dd3235[]={24403,0},
-dd3236[]={14209,0},
-dd3237[]={23648,0},
-dd3238[]={23744,0},
-dd3239[]={23693,0},
-dd3240[]={138724,0},
-dd3241[]={23875,0},
-dd3242[]={138726,0},
-dd3243[]={23918,0},
-dd3244[]={23915,0},
-dd3245[]={23932,0},
-dd3246[]={24033,0},
-dd3247[]={24034,0},
-dd3248[]={14383,0},
-dd3249[]={24061,0},
-dd3250[]={24104,0},
-dd3251[]={24125,0},
-dd3252[]={24169,0},
-dd3253[]={14434,0},
-dd3254[]={139651,0},
-dd3255[]={14460,0},
-dd3256[]={24240,0},
-dd3257[]={24243,0},
-dd3258[]={24246,0},
-dd3259[]={172946,0},
-dd3260[]={140081,0},
-dd3261[]={33281,0},
-dd3262[]={24354,0},
-dd3263[]={14535,0},
-dd3264[]={144056,0},
-dd3265[]={156122,0},
-dd3266[]={24418,0},
-dd3267[]={24427,0},
-dd3268[]={14563,0},
-dd3269[]={24474,0},
-dd3270[]={24525,0},
-dd3271[]={24535,0},
-dd3272[]={24569,0},
-dd3273[]={24705,0},
-dd3274[]={14650,0},
-dd3275[]={14620,0},
-dd3276[]={141012,0},
-dd3277[]={24775,0},
-dd3278[]={24904,0},
-dd3279[]={24908,0},
-dd3280[]={24954,0},
-dd3281[]={25010,0},
-dd3282[]={24996,0},
-dd3283[]={25007,0},
-dd3284[]={25054,0},
-dd3285[]={25104,0},
-dd3286[]={25115,0},
-dd3287[]={25181,0},
-dd3288[]={25265,0},
-dd3289[]={25300,0},
-dd3290[]={25424,0},
-dd3291[]={142092,0},
-dd3292[]={25405,0},
-dd3293[]={25340,0},
-dd3294[]={25448,0},
-dd3295[]={25475,0},
-dd3296[]={25572,0},
-dd3297[]={142321,0},
-dd3298[]={25634,0},
-dd3299[]={25541,0},
-dd3300[]={25513,0},
-dd3301[]={14894,0},
-dd3302[]={25705,0},
-dd3303[]={25726,0},
-dd3304[]={25757,0},
-dd3305[]={25719,0},
-dd3306[]={14956,0},
-dd3307[]={25964,0},
-dd3308[]={143370,0},
-dd3309[]={26083,0},
-dd3310[]={26360,0},
-dd3311[]={26185,0},
-dd3312[]={15129,0},
-dd3313[]={15112,0},
-dd3314[]={15076,0},
-dd3315[]={20882,0},
-dd3316[]={20885,0},
-dd3317[]={26368,0},
-dd3318[]={26268,0},
-dd3319[]={32941,0},
-dd3320[]={17369,0},
-dd3321[]={26401,0},
-dd3322[]={26462,0},
-dd3323[]={26451,0},
-dd3324[]={144323,0},
-dd3325[]={15177,0},
-dd3326[]={26618,0},
-dd3327[]={26501,0},
-dd3328[]={26706,0},
-dd3329[]={144493,0},
-dd3330[]={26766,0},
-dd3331[]={26655,0},
-dd3332[]={26900,0},
-dd3333[]={26946,0},
-dd3334[]={27043,0},
-dd3335[]={27114,0},
-dd3336[]={27304,0},
-dd3337[]={145059,0},
-dd3338[]={27355,0},
-dd3339[]={15384,0},
-dd3340[]={27425,0},
-dd3341[]={145575,0},
-dd3342[]={27476,0},
-dd3343[]={15438,0},
-dd3344[]={27506,0},
-dd3345[]={27551,0},
-dd3346[]={27579,0},
-dd3347[]={146061,0},
-dd3348[]={138507,0},
-dd3349[]={146170,0},
-dd3350[]={27726,0},
-dd3351[]={146620,0},
-dd3352[]={27839,0},
-dd3353[]={27853,0},
-dd3354[]={27751,0},
-dd3355[]={27926,0},
-dd3356[]={27966,0},
-dd3357[]={28009,0},
-dd3358[]={28024,0},
-dd3359[]={28037,0},
-dd3360[]={146718,0},
-dd3361[]={27956,0},
-dd3362[]={28207,0},
-dd3363[]={28270,0},
-dd3364[]={15667,0},
-dd3365[]={28359,0},
-dd3366[]={147153,0},
-dd3367[]={28153,0},
-dd3368[]={28526,0},
-dd3369[]={147294,0},
-dd3370[]={147342,0},
-dd3371[]={28614,0},
-dd3372[]={28729,0},
-dd3373[]={28699,0},
-dd3374[]={15766,0},
-dd3375[]={28746,0},
-dd3376[]={28797,0},
-dd3377[]={28791,0},
-dd3378[]={28845,0},
-dd3379[]={132389,0},
-dd3380[]={28997,0},
-dd3381[]={148067,0},
-dd3382[]={29084,0},
-dd3383[]={148395,0},
-dd3384[]={29224,0},
-dd3385[]={29264,0},
-dd3386[]={149000,0},
-dd3387[]={29312,0},
-dd3388[]={29333,0},
-dd3389[]={149301,0},
-dd3390[]={149524,0},
-dd3391[]={29562,0},
-dd3392[]={29579,0},
-dd3393[]={16044,0},
-dd3394[]={29605,0},
-dd3395[]={16056,0},
-dd3396[]={29767,0},
-dd3397[]={29788,0},
-dd3398[]={29829,0},
-dd3399[]={29898,0},
-dd3400[]={16155,0},
-dd3401[]={29988,0},
-dd3402[]={150582,0},
-dd3403[]={30014,0},
-dd3404[]={150674,0},
-dd3405[]={139679,0},
-dd3406[]={30224,0},
-dd3407[]={151457,0},
-dd3408[]={151480,0},
-dd3409[]={151620,0},
-dd3410[]={16380,0},
-dd3411[]={16392,0},
-dd3412[]={151795,0},
-dd3413[]={151794,0},
-dd3414[]={151833,0},
-dd3415[]={151859,0},
-dd3416[]={30494,0},
-dd3417[]={30495,0},
-dd3418[]={30603,0},
-dd3419[]={16454,0},
-dd3420[]={16534,0},
-dd3421[]={152605,0},
-dd3422[]={30798,0},
-dd3423[]={16611,0},
-dd3424[]={153126,0},
-dd3425[]={153242,0},
-dd3426[]={153285,0},
-dd3427[]={31211,0},
-dd3428[]={16687,0},
-dd3429[]={31306,0},
-dd3430[]={31311,0},
-dd3431[]={153980,0},
-dd3432[]={154279,0},
-dd3433[]={31470,0},
-dd3434[]={16898,0},
-dd3435[]={154539,0},
-dd3436[]={31686,0},
-dd3437[]={31689,0},
-dd3438[]={16935,0},
-dd3439[]={154752,0},
-dd3440[]={31954,0},
-dd3441[]={17056,0},
-dd3442[]={31976,0},
-dd3443[]={31971,0},
-dd3444[]={32000,0},
-dd3445[]={155526,0},
-dd3446[]={32099,0},
-dd3447[]={17153,0},
-dd3448[]={32199,0},
-dd3449[]={32258,0},
-dd3450[]={32325,0},
-dd3451[]={17204,0},
-dd3452[]={156200,0},
-dd3453[]={156231,0},
-dd3454[]={17241,0},
-dd3455[]={156377,0},
-dd3456[]={32634,0},
-dd3457[]={156478,0},
-dd3458[]={32661,0},
-dd3459[]={32762,0},
-dd3460[]={156890,0},
-dd3461[]={156963,0},
-dd3462[]={32864,0},
-dd3463[]={157096,0},
-dd3464[]={32880,0},
-dd3465[]={144223,0},
-dd3466[]={17365,0},
-dd3467[]={32946,0},
-dd3468[]={33027,0},
-dd3469[]={17419,0},
-dd3470[]={33086,0},
-dd3471[]={23221,0},
-dd3472[]={157607,0},
-dd3473[]={157621,0},
-dd3474[]={144275,0},
-dd3475[]={144284,0},
-dd3476[]={33284,0},
-dd3477[]={36766,0},
-dd3478[]={17515,0},
-dd3479[]={33425,0},
-dd3480[]={33419,0},
-dd3481[]={33437,0},
-dd3482[]={21171,0},
-dd3483[]={33457,0},
-dd3484[]={33459,0},
-dd3485[]={33469,0},
-dd3486[]={33510,0},
-dd3487[]={158524,0},
-dd3488[]={33565,0},
-dd3489[]={33635,0},
-dd3490[]={33709,0},
-dd3491[]={33571,0},
-dd3492[]={33725,0},
-dd3493[]={33767,0},
-dd3494[]={33619,0},
-dd3495[]={33738,0},
-dd3496[]={33740,0},
-dd3497[]={33756,0},
-dd3498[]={158774,0},
-dd3499[]={159083,0},
-dd3500[]={158933,0},
-dd3501[]={17707,0},
-dd3502[]={34033,0},
-dd3503[]={34035,0},
-dd3504[]={34070,0},
-dd3505[]={160714,0},
-dd3506[]={34148,0},
-dd3507[]={159532,0},
-dd3508[]={17757,0},
-dd3509[]={17761,0},
-dd3510[]={159665,0},
-dd3511[]={159954,0},
-dd3512[]={17771,0},
-dd3513[]={34384,0},
-dd3514[]={34407,0},
-dd3515[]={34409,0},
-dd3516[]={34473,0},
-dd3517[]={34440,0},
-dd3518[]={34574,0},
-dd3519[]={34530,0},
-dd3520[]={34600,0},
-dd3521[]={34667,0},
-dd3522[]={34694,0},
-dd3523[]={17879,0},
-dd3524[]={34785,0},
-dd3525[]={34817,0},
-dd3526[]={17913,0},
-dd3527[]={34912,0},
-dd3528[]={161383,0},
-dd3529[]={35031,0},
-dd3530[]={35038,0},
-dd3531[]={17973,0},
-dd3532[]={35066,0},
-dd3533[]={13499,0},
-dd3534[]={161966,0},
-dd3535[]={162150,0},
-dd3536[]={18110,0},
-dd3537[]={18119,0},
-dd3538[]={35488,0},
-dd3539[]={162984,0},
-dd3540[]={36011,0},
-dd3541[]={36033,0},
-dd3542[]={36123,0},
-dd3543[]={36215,0},
-dd3544[]={163631,0},
-dd3545[]={133124,0},
-dd3546[]={36299,0},
-dd3547[]={36284,0},
-dd3548[]={36336,0},
-dd3549[]={133342,0},
-dd3550[]={36564,0},
-dd3551[]={165330,0},
-dd3552[]={165357,0},
-dd3553[]={37012,0},
-dd3554[]={37105,0},
-dd3555[]={37137,0},
-dd3556[]={165678,0},
-dd3557[]={37147,0},
-dd3558[]={37432,0},
-dd3559[]={37591,0},
-dd3560[]={37592,0},
-dd3561[]={37500,0},
-dd3562[]={37881,0},
-dd3563[]={37909,0},
-dd3564[]={166906,0},
-dd3565[]={38283,0},
-dd3566[]={18837,0},
-dd3567[]={38327,0},
-dd3568[]={167287,0},
-dd3569[]={18918,0},
-dd3570[]={38595,0},
-dd3571[]={23986,0},
-dd3572[]={38691,0},
-dd3573[]={168261,0},
-dd3574[]={168474,0},
-dd3575[]={19054,0},
-dd3576[]={19062,0},
-dd3577[]={38880,0},
-dd3578[]={168970,0},
-dd3579[]={19122,0},
-dd3580[]={169110,0},
-dd3581[]={38953,0},
-dd3582[]={169398,0},
-dd3583[]={39138,0},
-dd3584[]={19251,0},
-dd3585[]={39209,0},
-dd3586[]={39335,0},
-dd3587[]={39362,0},
-dd3588[]={39422,0},
-dd3589[]={19406,0},
-dd3590[]={170800,0},
-dd3591[]={40000,0},
-dd3592[]={40189,0},
-dd3593[]={19662,0},
-dd3594[]={19693,0},
-dd3595[]={40295,0},
-dd3596[]={172238,0},
-dd3597[]={19704,0},
-dd3598[]={172293,0},
-dd3599[]={172558,0},
-dd3600[]={172689,0},
-dd3601[]={19798,0},
-dd3602[]={40702,0},
-dd3603[]={40709,0},
-dd3604[]={40719,0},
-dd3605[]={40726,0},
-dd3606[]={173568,0},
-dd3607[]={8814,0},
-dd3608[]={8800,0},
-dd3609[]={8815,0},
-dd3610[]={192,193,194,195,196,197,256,258,260,461,512,514,550,7680,7840,7842,0},
-dd3611[]={7682,7684,7686,0},
-dd3612[]={199,262,264,266,268,0},
-dd3613[]={270,7690,7692,7694,7696,7698,0},
-dd3614[]={200,201,202,203,274,276,278,280,282,516,518,552,7704,7706,7864,7866,7868,0},
-dd3615[]={7710,0},
-dd3616[]={284,286,288,290,486,500,7712,0},
-dd3617[]={292,542,7714,7716,7718,7720,7722,0},
-dd3618[]={204,205,206,207,296,298,300,302,304,463,520,522,7724,7880,7882,0},
-dd3619[]={308,0},
-dd3620[]={310,488,7728,7730,7732,0},
-dd3621[]={313,315,317,7734,7738,7740,0},
-dd3622[]={7742,7744,7746,0},
-dd3623[]={209,323,325,327,504,7748,7750,7752,7754,0},
-dd3624[]={210,211,212,213,214,332,334,336,416,465,490,524,526,558,7884,7886,0},
-dd3625[]={7764,7766,0},
-dd3626[]={340,342,344,528,530,7768,7770,7774,0},
-dd3627[]={346,348,350,352,536,7776,7778,0},
-dd3628[]={354,356,538,7786,7788,7790,7792,0},
-dd3629[]={217,218,219,220,360,362,364,366,368,370,431,467,532,534,7794,7796,7798,7908,7910,0},
-dd3630[]={7804,7806,0},
-dd3631[]={372,7808,7810,7812,7814,7816,0},
-dd3632[]={7818,7820,0},
-dd3633[]={221,374,376,562,7822,7922,7924,7926,7928,0},
-dd3634[]={377,379,381,7824,7826,7828,0},
-dd3635[]={224,225,226,227,228,229,257,259,261,462,513,515,551,7681,7841,7843,0},
-dd3636[]={7683,7685,7687,0},
-dd3637[]={231,263,265,267,269,0},
-dd3638[]={271,7691,7693,7695,7697,7699,0},
-dd3639[]={232,233,234,235,275,277,279,281,283,517,519,553,7705,7707,7865,7867,7869,0},
-dd3640[]={7711,0},
-dd3641[]={285,287,289,291,487,501,7713,0},
-dd3642[]={293,543,7715,7717,7719,7721,7723,7830,0},
-dd3643[]={236,237,238,239,297,299,301,303,464,521,523,7725,7881,7883,0},
-dd3644[]={309,496,0},
-dd3645[]={311,489,7729,7731,7733,0},
-dd3646[]={314,316,318,7735,7739,7741,0},
-dd3647[]={7743,7745,7747,0},
-dd3648[]={241,324,326,328,505,7749,7751,7753,7755,0},
-dd3649[]={242,243,244,245,246,333,335,337,417,466,491,525,527,559,7885,7887,0},
-dd3650[]={7765,7767,0},
-dd3651[]={341,343,345,529,531,7769,7771,7775,0},
-dd3652[]={347,349,351,353,537,7777,7779,0},
-dd3653[]={355,357,539,7787,7789,7791,7793,7831,0},
-dd3654[]={249,250,251,252,361,363,365,367,369,371,432,468,533,535,7795,7797,7799,7909,7911,0},
-dd3655[]={7805,7807,0},
-dd3656[]={373,7809,7811,7813,7815,7817,7832,0},
-dd3657[]={7819,7821,0},
-dd3658[]={253,255,375,563,7823,7833,7923,7925,7927,7929,0},
-dd3659[]={378,380,382,7825,7827,7829,0},
-dd3660[]={901,8129,8173,0},
-dd3661[]={7844,7846,7848,7850,0},
-dd3662[]={478,0},
-dd3663[]={506,0},
-dd3664[]={482,508,0},
-dd3665[]={7688,0},
-dd3666[]={7870,7872,7874,7876,0},
-dd3667[]={7726,0},
-dd3668[]={7888,7890,7892,7894,0},
-dd3669[]={556,7756,7758,0},
-dd3670[]={554,0},
-dd3671[]={510,0},
-dd3672[]={469,471,473,475,0},
-dd3673[]={7845,7847,7849,7851,0},
-dd3674[]={479,0},
-dd3675[]={507,0},
-dd3676[]={483,509,0},
-dd3677[]={7689,0},
-dd3678[]={7871,7873,7875,7877,0},
-dd3679[]={7727,0},
-dd3680[]={7889,7891,7893,7895,0},
-dd3681[]={557,7757,7759,0},
-dd3682[]={555,0},
-dd3683[]={511,0},
-dd3684[]={470,472,474,476,0},
-dd3685[]={7854,7856,7858,7860,0},
-dd3686[]={7855,7857,7859,7861,0},
-dd3687[]={7700,7702,0},
-dd3688[]={7701,7703,0},
-dd3689[]={7760,7762,0},
-dd3690[]={7761,7763,0},
-dd3691[]={7780,0},
-dd3692[]={7781,0},
-dd3693[]={7782,0},
-dd3694[]={7783,0},
-dd3695[]={7800,0},
-dd3696[]={7801,0},
-dd3697[]={7802,0},
-dd3698[]={7803,0},
-dd3699[]={7835,0},
-dd3700[]={7898,7900,7902,7904,7906,0},
-dd3701[]={7899,7901,7903,7905,7907,0},
-dd3702[]={7912,7914,7916,7918,7920,0},
-dd3703[]={7913,7915,7917,7919,7921,0},
-dd3704[]={494,0},
-dd3705[]={492,0},
-dd3706[]={493,0},
-dd3707[]={480,0},
-dd3708[]={481,0},
-dd3709[]={7708,0},
-dd3710[]={7709,0},
-dd3711[]={560,0},
-dd3712[]={561,0},
-dd3713[]={495,0},
-dd3714[]={902,7944,7945,8120,8121,8122,8124,0},
-dd3715[]={904,7960,7961,8136,0},
-dd3716[]={905,7976,7977,8138,8140,0},
-dd3717[]={906,938,7992,7993,8152,8153,8154,0},
-dd3718[]={908,8008,8009,8184,0},
-dd3719[]={8172,0},
-dd3720[]={910,939,8025,8168,8169,8170,0},
-dd3721[]={911,8040,8041,8186,8188,0},
-dd3722[]={8116,0},
-dd3723[]={8132,0},
-dd3724[]={940,7936,7937,8048,8112,8113,8115,8118,0},
-dd3725[]={941,7952,7953,8050,0},
-dd3726[]={942,7968,7969,8052,8131,8134,0},
-dd3727[]={943,970,7984,7985,8054,8144,8145,8150,0},
-dd3728[]={972,8000,8001,8056,0},
-dd3729[]={8164,8165,0},
-dd3730[]={971,973,8016,8017,8058,8160,8161,8166,0},
-dd3731[]={974,8032,8033,8060,8179,8182,0},
-dd3732[]={912,8146,8151,0},
-dd3733[]={944,8162,8167,0},
-dd3734[]={8180,0},
-dd3735[]={979,980,0},
-dd3736[]={1031,0},
-dd3737[]={1232,1234,0},
-dd3738[]={1027,0},
-dd3739[]={1024,1025,1238,0},
-dd3740[]={1217,1244,0},
-dd3741[]={1246,0},
-dd3742[]={1037,1049,1250,1252,0},
-dd3743[]={1036,0},
-dd3744[]={1254,0},
-dd3745[]={1038,1262,1264,1266,0},
-dd3746[]={1268,0},
-dd3747[]={1272,0},
-dd3748[]={1260,0},
-dd3749[]={1233,1235,0},
-dd3750[]={1107,0},
-dd3751[]={1104,1105,1239,0},
-dd3752[]={1218,1245,0},
-dd3753[]={1247,0},
-dd3754[]={1081,1117,1251,1253,0},
-dd3755[]={1116,0},
-dd3756[]={1255,0},
-dd3757[]={1118,1263,1265,1267,0},
-dd3758[]={1269,0},
-dd3759[]={1273,0},
-dd3760[]={1261,0},
-dd3761[]={1111,0},
-dd3762[]={1142,0},
-dd3763[]={1143,0},
-dd3764[]={1242,0},
-dd3765[]={1243,0},
-dd3766[]={1258,0},
-dd3767[]={1259,0},
-dd3768[]={1570,1571,1573,0},
-dd3769[]={1730,0},
-dd3770[]={2345,0},
-dd3771[]={2353,0},
-dd3772[]={2356,0},
-dd3773[]={2507,2508,0},
-dd3774[]={2888,2891,2892,0},
-dd3775[]={2964,0},
-dd3776[]={3018,3020,0},
-dd3777[]={3019,0},
-dd3778[]={3144,0},
-dd3779[]={3264,0},
-dd3780[]={3271,3272,3274,0},
-dd3781[]={3275,0},
-dd3782[]={3402,3404,0},
-dd3783[]={3403,0},
-dd3784[]={3546,3548,3550,0},
-dd3785[]={3549,0},
-dd3786[]={4134,0},
-dd3787[]={6918,0},
-dd3788[]={6920,0},
-dd3789[]={6922,0},
-dd3790[]={6924,0},
-dd3791[]={6926,0},
-dd3792[]={6930,0},
-dd3793[]={6971,0},
-dd3794[]={6973,0},
-dd3795[]={6976,0},
-dd3796[]={6977,0},
-dd3797[]={6979,0},
-dd3798[]={7736,0},
-dd3799[]={7737,0},
-dd3800[]={7772,0},
-dd3801[]={7773,0},
-dd3802[]={7784,0},
-dd3803[]={7785,0},
-dd3804[]={7852,7862,0},
-dd3805[]={7853,7863,0},
-dd3806[]={7878,0},
-dd3807[]={7879,0},
-dd3808[]={7896,0},
-dd3809[]={7897,0},
-dd3810[]={7938,7940,7942,8064,0},
-dd3811[]={7939,7941,7943,8065,0},
-dd3812[]={8066,0},
-dd3813[]={8067,0},
-dd3814[]={8068,0},
-dd3815[]={8069,0},
-dd3816[]={8070,0},
-dd3817[]={8071,0},
-dd3818[]={7946,7948,7950,8072,0},
-dd3819[]={7947,7949,7951,8073,0},
-dd3820[]={8074,0},
-dd3821[]={8075,0},
-dd3822[]={8076,0},
-dd3823[]={8077,0},
-dd3824[]={8078,0},
-dd3825[]={8079,0},
-dd3826[]={7954,7956,0},
-dd3827[]={7955,7957,0},
-dd3828[]={7962,7964,0},
-dd3829[]={7963,7965,0},
-dd3830[]={7970,7972,7974,8080,0},
-dd3831[]={7971,7973,7975,8081,0},
-dd3832[]={8082,0},
-dd3833[]={8083,0},
-dd3834[]={8084,0},
-dd3835[]={8085,0},
-dd3836[]={8086,0},
-dd3837[]={8087,0},
-dd3838[]={7978,7980,7982,8088,0},
-dd3839[]={7979,7981,7983,8089,0},
-dd3840[]={8090,0},
-dd3841[]={8091,0},
-dd3842[]={8092,0},
-dd3843[]={8093,0},
-dd3844[]={8094,0},
-dd3845[]={8095,0},
-dd3846[]={7986,7988,7990,0},
-dd3847[]={7987,7989,7991,0},
-dd3848[]={7994,7996,7998,0},
-dd3849[]={7995,7997,7999,0},
-dd3850[]={8002,8004,0},
-dd3851[]={8003,8005,0},
-dd3852[]={8010,8012,0},
-dd3853[]={8011,8013,0},
-dd3854[]={8018,8020,8022,0},
-dd3855[]={8019,8021,8023,0},
-dd3856[]={8027,8029,8031,0},
-dd3857[]={8034,8036,8038,8096,0},
-dd3858[]={8035,8037,8039,8097,0},
-dd3859[]={8098,0},
-dd3860[]={8099,0},
-dd3861[]={8100,0},
-dd3862[]={8101,0},
-dd3863[]={8102,0},
-dd3864[]={8103,0},
-dd3865[]={8042,8044,8046,8104,0},
-dd3866[]={8043,8045,8047,8105,0},
-dd3867[]={8106,0},
-dd3868[]={8107,0},
-dd3869[]={8108,0},
-dd3870[]={8109,0},
-dd3871[]={8110,0},
-dd3872[]={8111,0},
-dd3873[]={8114,0},
-dd3874[]={8130,0},
-dd3875[]={8178,0},
-dd3876[]={8119,0},
-dd3877[]={8141,8142,8143,0},
-dd3878[]={8135,0},
-dd3879[]={8183,0},
-dd3880[]={8157,8158,8159,0},
-dd3881[]={8602,0},
-dd3882[]={8603,0},
-dd3883[]={8622,0},
-dd3884[]={8653,0},
-dd3885[]={8655,0},
-dd3886[]={8654,0},
-dd3887[]={8708,0},
-dd3888[]={8713,0},
-dd3889[]={8716,0},
-dd3890[]={8740,0},
-dd3891[]={8742,0},
-dd3892[]={8769,0},
-dd3893[]={8772,0},
-dd3894[]={8775,0},
-dd3895[]={8777,0},
-dd3896[]={8813,0},
-dd3897[]={8802,0},
-dd3898[]={8816,0},
-dd3899[]={8817,0},
-dd3900[]={8820,0},
-dd3901[]={8821,0},
-dd3902[]={8824,0},
-dd3903[]={8825,0},
-dd3904[]={8832,0},
-dd3905[]={8833,0},
-dd3906[]={8928,0},
-dd3907[]={8929,0},
-dd3908[]={8836,0},
-dd3909[]={8837,0},
-dd3910[]={8840,0},
-dd3911[]={8841,0},
-dd3912[]={8930,0},
-dd3913[]={8931,0},
-dd3914[]={8876,0},
-dd3915[]={8877,0},
-dd3916[]={8878,0},
-dd3917[]={8879,0},
-dd3918[]={8938,0},
-dd3919[]={8939,0},
-dd3920[]={8940,0},
-dd3921[]={8941,0},
-dd3922[]={12436,0},
-dd3923[]={12364,0},
-dd3924[]={12366,0},
-dd3925[]={12368,0},
-dd3926[]={12370,0},
-dd3927[]={12372,0},
-dd3928[]={12374,0},
-dd3929[]={12376,0},
-dd3930[]={12378,0},
-dd3931[]={12380,0},
-dd3932[]={12382,0},
-dd3933[]={12384,0},
-dd3934[]={12386,0},
-dd3935[]={12389,0},
-dd3936[]={12391,0},
-dd3937[]={12393,0},
-dd3938[]={12400,12401,0},
-dd3939[]={12403,12404,0},
-dd3940[]={12406,12407,0},
-dd3941[]={12409,12410,0},
-dd3942[]={12412,12413,0},
-dd3943[]={12446,0},
-dd3944[]={12532,0},
-dd3945[]={12460,0},
-dd3946[]={12462,0},
-dd3947[]={12464,0},
-dd3948[]={12466,0},
-dd3949[]={12468,0},
-dd3950[]={12470,0},
-dd3951[]={12472,0},
-dd3952[]={12474,0},
-dd3953[]={12476,0},
-dd3954[]={12478,0},
-dd3955[]={12480,0},
-dd3956[]={12482,0},
-dd3957[]={12485,0},
-dd3958[]={12487,0},
-dd3959[]={12489,0},
-dd3960[]={12496,12497,0},
-dd3961[]={12499,12500,0},
-dd3962[]={12502,12503,0},
-dd3963[]={12505,12506,0},
-dd3964[]={12508,12509,0},
-dd3965[]={12535,0},
-dd3966[]={12536,0},
-dd3967[]={12537,0},
-dd3968[]={12538,0},
-dd3969[]={12542,0},
-dd3970[]={224,0},
-dd3971[]={225,0},
-dd3972[]={226,0},
-dd3973[]={227,0},
-dd3974[]={228,0},
-dd3975[]={229,0},
-dd3976[]={230,0},
-dd3977[]={231,0},
-dd3978[]={232,0},
-dd3979[]={233,0},
-dd3980[]={234,0},
-dd3981[]={235,0},
-dd3982[]={236,0},
-dd3983[]={237,0},
-dd3984[]={238,0},
-dd3985[]={239,0},
-dd3986[]={241,0},
-dd3987[]={242,0},
-dd3988[]={243,0},
-dd3989[]={244,0},
-dd3990[]={245,0},
-dd3991[]={246,0},
-dd3992[]={248,0},
-dd3993[]={249,0},
-dd3994[]={250,0},
-dd3995[]={251,0},
-dd3996[]={252,0},
-dd3997[]={253,0},
-dd3998[]={254,0},
-dd3999[]={115,115,0},
-dd4000[]={257,0},
-dd4001[]={259,0},
-dd4002[]={261,0},
-dd4003[]={263,0},
-dd4004[]={265,0},
-dd4005[]={267,0},
-dd4006[]={269,0},
-dd4007[]={271,0},
-dd4008[]={273,0},
-dd4009[]={275,0},
-dd4010[]={277,0},
-dd4011[]={279,0},
-dd4012[]={281,0},
-dd4013[]={283,0},
-dd4014[]={285,0},
-dd4015[]={287,0},
-dd4016[]={289,0},
-dd4017[]={291,0},
-dd4018[]={293,0},
-dd4019[]={297,0},
-dd4020[]={299,0},
-dd4021[]={301,0},
-dd4022[]={303,0},
-dd4023[]={105,775,0},
-dd4024[]={307,0},
-dd4025[]={309,0},
-dd4026[]={311,0},
-dd4027[]={314,0},
-dd4028[]={316,0},
-dd4029[]={318,0},
-dd4030[]={320,0},
-dd4031[]={322,0},
-dd4032[]={324,0},
-dd4033[]={326,0},
-dd4034[]={328,0},
-dd4035[]={333,0},
-dd4036[]={335,0},
-dd4037[]={337,0},
-dd4038[]={339,0},
-dd4039[]={341,0},
-dd4040[]={343,0},
-dd4041[]={345,0},
-dd4042[]={347,0},
-dd4043[]={349,0},
-dd4044[]={351,0},
-dd4045[]={353,0},
-dd4046[]={355,0},
-dd4047[]={357,0},
-dd4048[]={359,0},
-dd4049[]={361,0},
-dd4050[]={363,0},
-dd4051[]={365,0},
-dd4052[]={367,0},
-dd4053[]={369,0},
-dd4054[]={371,0},
-dd4055[]={373,0},
-dd4056[]={375,0},
-dd4057[]={255,0},
-dd4058[]={378,0},
-dd4059[]={380,0},
-dd4060[]={382,0},
-dd4061[]={595,0},
-dd4062[]={387,0},
-dd4063[]={389,0},
-dd4064[]={392,0},
-dd4065[]={598,0},
-dd4066[]={599,0},
-dd4067[]={396,0},
-dd4068[]={477,0},
-dd4069[]={402,0},
-dd4070[]={608,0},
-dd4071[]={409,0},
-dd4072[]={417,0},
-dd4073[]={419,0},
-dd4074[]={421,0},
-dd4075[]={640,0},
-dd4076[]={424,0},
-dd4077[]={429,0},
-dd4078[]={648,0},
-dd4079[]={432,0},
-dd4080[]={436,0},
-dd4081[]={438,0},
-dd4082[]={441,0},
-dd4083[]={445,0},
-dd4084[]={454,0},
-dd4085[]={457,0},
-dd4086[]={460,0},
-dd4087[]={462,0},
-dd4088[]={464,0},
-dd4089[]={466,0},
-dd4090[]={468,0},
-dd4091[]={470,0},
-dd4092[]={472,0},
-dd4093[]={474,0},
-dd4094[]={476,0},
-dd4095[]={483,0},
-dd4096[]={485,0},
-dd4097[]={487,0},
-dd4098[]={489,0},
-dd4099[]={491,0},
-dd4100[]={499,0},
-dd4101[]={501,0},
-dd4102[]={405,0},
-dd4103[]={447,0},
-dd4104[]={505,0},
-dd4105[]={509,0},
-dd4106[]={513,0},
-dd4107[]={515,0},
-dd4108[]={517,0},
-dd4109[]={519,0},
-dd4110[]={521,0},
-dd4111[]={523,0},
-dd4112[]={525,0},
-dd4113[]={527,0},
-dd4114[]={529,0},
-dd4115[]={531,0},
-dd4116[]={533,0},
-dd4117[]={535,0},
-dd4118[]={537,0},
-dd4119[]={539,0},
-dd4120[]={541,0},
-dd4121[]={543,0},
-dd4122[]={414,0},
-dd4123[]={547,0},
-dd4124[]={549,0},
-dd4125[]={551,0},
-dd4126[]={553,0},
-dd4127[]={557,0},
-dd4128[]={559,0},
-dd4129[]={563,0},
-dd4130[]={11365,0},
-dd4131[]={572,0},
-dd4132[]={410,0},
-dd4133[]={11366,0},
-dd4134[]={578,0},
-dd4135[]={384,0},
-dd4136[]={583,0},
-dd4137[]={585,0},
-dd4138[]={587,0},
-dd4139[]={589,0},
-dd4140[]={591,0},
-dd4141[]={953,776,769,0},
-dd4142[]={970,0},
-dd4143[]={971,0},
-dd4144[]={965,776,769,0},
-dd4145[]={985,0},
-dd4146[]={987,0},
-dd4147[]={991,0},
-dd4148[]={993,0},
-dd4149[]={995,0},
-dd4150[]={997,0},
-dd4151[]={999,0},
-dd4152[]={1001,0},
-dd4153[]={1003,0},
-dd4154[]={1005,0},
-dd4155[]={1007,0},
-dd4156[]={1016,0},
-dd4157[]={1010,0},
-dd4158[]={1019,0},
-dd4159[]={891,0},
-dd4160[]={892,0},
-dd4161[]={893,0},
-dd4162[]={1104,0},
-dd4163[]={1105,0},
-dd4164[]={1106,0},
-dd4165[]={1108,0},
-dd4166[]={1109,0},
-dd4167[]={1110,0},
-dd4168[]={1112,0},
-dd4169[]={1113,0},
-dd4170[]={1114,0},
-dd4171[]={1115,0},
-dd4172[]={1117,0},
-dd4173[]={1118,0},
-dd4174[]={1119,0},
-dd4175[]={1072,0},
-dd4176[]={1073,0},
-dd4177[]={1074,0},
-dd4178[]={1075,0},
-dd4179[]={1076,0},
-dd4180[]={1077,0},
-dd4181[]={1078,0},
-dd4182[]={1079,0},
-dd4183[]={1080,0},
-dd4184[]={1081,0},
-dd4185[]={1082,0},
-dd4186[]={1083,0},
-dd4187[]={1084,0},
-dd4188[]={1086,0},
-dd4189[]={1087,0},
-dd4190[]={1088,0},
-dd4191[]={1089,0},
-dd4192[]={1090,0},
-dd4193[]={1091,0},
-dd4194[]={1092,0},
-dd4195[]={1093,0},
-dd4196[]={1094,0},
-dd4197[]={1095,0},
-dd4198[]={1096,0},
-dd4199[]={1097,0},
-dd4200[]={1098,0},
-dd4201[]={1099,0},
-dd4202[]={1100,0},
-dd4203[]={1101,0},
-dd4204[]={1102,0},
-dd4205[]={1103,0},
-dd4206[]={1121,0},
-dd4207[]={1123,0},
-dd4208[]={1125,0},
-dd4209[]={1127,0},
-dd4210[]={1129,0},
-dd4211[]={1131,0},
-dd4212[]={1133,0},
-dd4213[]={1135,0},
-dd4214[]={1137,0},
-dd4215[]={1139,0},
-dd4216[]={1141,0},
-dd4217[]={1145,0},
-dd4218[]={1147,0},
-dd4219[]={1149,0},
-dd4220[]={1151,0},
-dd4221[]={1153,0},
-dd4222[]={1163,0},
-dd4223[]={1165,0},
-dd4224[]={1167,0},
-dd4225[]={1169,0},
-dd4226[]={1171,0},
-dd4227[]={1173,0},
-dd4228[]={1175,0},
-dd4229[]={1177,0},
-dd4230[]={1179,0},
-dd4231[]={1181,0},
-dd4232[]={1183,0},
-dd4233[]={1185,0},
-dd4234[]={1187,0},
-dd4235[]={1189,0},
-dd4236[]={1191,0},
-dd4237[]={1193,0},
-dd4238[]={1195,0},
-dd4239[]={1197,0},
-dd4240[]={1199,0},
-dd4241[]={1201,0},
-dd4242[]={1203,0},
-dd4243[]={1205,0},
-dd4244[]={1207,0},
-dd4245[]={1209,0},
-dd4246[]={1211,0},
-dd4247[]={1213,0},
-dd4248[]={1215,0},
-dd4249[]={1231,0},
-dd4250[]={1218,0},
-dd4251[]={1220,0},
-dd4252[]={1222,0},
-dd4253[]={1224,0},
-dd4254[]={1226,0},
-dd4255[]={1228,0},
-dd4256[]={1230,0},
-dd4257[]={1233,0},
-dd4258[]={1235,0},
-dd4259[]={1237,0},
-dd4260[]={1239,0},
-dd4261[]={1241,0},
-dd4262[]={1245,0},
-dd4263[]={1249,0},
-dd4264[]={1251,0},
-dd4265[]={1253,0},
-dd4266[]={1257,0},
-dd4267[]={1263,0},
-dd4268[]={1265,0},
-dd4269[]={1267,0},
-dd4270[]={1271,0},
-dd4271[]={1275,0},
-dd4272[]={1277,0},
-dd4273[]={1279,0},
-dd4274[]={1281,0},
-dd4275[]={1283,0},
-dd4276[]={1285,0},
-dd4277[]={1287,0},
-dd4278[]={1289,0},
-dd4279[]={1291,0},
-dd4280[]={1293,0},
-dd4281[]={1295,0},
-dd4282[]={1297,0},
-dd4283[]={1299,0},
-dd4284[]={1377,0},
-dd4285[]={1378,0},
-dd4286[]={1379,0},
-dd4287[]={1380,0},
-dd4288[]={1381,0},
-dd4289[]={1382,0},
-dd4290[]={1383,0},
-dd4291[]={1384,0},
-dd4292[]={1385,0},
-dd4293[]={1386,0},
-dd4294[]={1387,0},
-dd4295[]={1388,0},
-dd4296[]={1389,0},
-dd4297[]={1390,0},
-dd4298[]={1391,0},
-dd4299[]={1392,0},
-dd4300[]={1393,0},
-dd4301[]={1394,0},
-dd4302[]={1395,0},
-dd4303[]={1396,0},
-dd4304[]={1397,0},
-dd4305[]={1398,0},
-dd4306[]={1399,0},
-dd4307[]={1400,0},
-dd4308[]={1401,0},
-dd4309[]={1402,0},
-dd4310[]={1403,0},
-dd4311[]={1404,0},
-dd4312[]={1405,0},
-dd4313[]={1406,0},
-dd4314[]={1407,0},
-dd4315[]={1408,0},
-dd4316[]={1409,0},
-dd4317[]={1410,0},
-dd4318[]={1411,0},
-dd4319[]={1412,0},
-dd4320[]={1413,0},
-dd4321[]={1414,0},
-dd4322[]={11520,0},
-dd4323[]={11521,0},
-dd4324[]={11522,0},
-dd4325[]={11523,0},
-dd4326[]={11524,0},
-dd4327[]={11525,0},
-dd4328[]={11526,0},
-dd4329[]={11527,0},
-dd4330[]={11528,0},
-dd4331[]={11529,0},
-dd4332[]={11530,0},
-dd4333[]={11531,0},
-dd4334[]={11532,0},
-dd4335[]={11533,0},
-dd4336[]={11534,0},
-dd4337[]={11535,0},
-dd4338[]={11536,0},
-dd4339[]={11537,0},
-dd4340[]={11538,0},
-dd4341[]={11539,0},
-dd4342[]={11540,0},
-dd4343[]={11541,0},
-dd4344[]={11542,0},
-dd4345[]={11543,0},
-dd4346[]={11544,0},
-dd4347[]={11545,0},
-dd4348[]={11546,0},
-dd4349[]={11547,0},
-dd4350[]={11548,0},
-dd4351[]={11549,0},
-dd4352[]={11550,0},
-dd4353[]={11551,0},
-dd4354[]={11552,0},
-dd4355[]={11553,0},
-dd4356[]={11554,0},
-dd4357[]={11555,0},
-dd4358[]={11556,0},
-dd4359[]={11557,0},
-dd4360[]={7681,0},
-dd4361[]={7683,0},
-dd4362[]={7685,0},
-dd4363[]={7687,0},
-dd4364[]={7691,0},
-dd4365[]={7693,0},
-dd4366[]={7695,0},
-dd4367[]={7697,0},
-dd4368[]={7699,0},
-dd4369[]={7701,0},
-dd4370[]={7703,0},
-dd4371[]={7705,0},
-dd4372[]={7707,0},
-dd4373[]={7713,0},
-dd4374[]={7715,0},
-dd4375[]={7717,0},
-dd4376[]={7719,0},
-dd4377[]={7721,0},
-dd4378[]={7723,0},
-dd4379[]={7725,0},
-dd4380[]={7729,0},
-dd4381[]={7731,0},
-dd4382[]={7733,0},
-dd4383[]={7735,0},
-dd4384[]={7739,0},
-dd4385[]={7741,0},
-dd4386[]={7743,0},
-dd4387[]={7745,0},
-dd4388[]={7747,0},
-dd4389[]={7749,0},
-dd4390[]={7751,0},
-dd4391[]={7753,0},
-dd4392[]={7755,0},
-dd4393[]={7757,0},
-dd4394[]={7759,0},
-dd4395[]={7761,0},
-dd4396[]={7763,0},
-dd4397[]={7765,0},
-dd4398[]={7767,0},
-dd4399[]={7769,0},
-dd4400[]={7771,0},
-dd4401[]={7775,0},
-dd4402[]={7777,0},
-dd4403[]={7779,0},
-dd4404[]={7787,0},
-dd4405[]={7789,0},
-dd4406[]={7791,0},
-dd4407[]={7793,0},
-dd4408[]={7795,0},
-dd4409[]={7797,0},
-dd4410[]={7799,0},
-dd4411[]={7805,0},
-dd4412[]={7807,0},
-dd4413[]={7809,0},
-dd4414[]={7811,0},
-dd4415[]={7813,0},
-dd4416[]={7815,0},
-dd4417[]={7817,0},
-dd4418[]={7819,0},
-dd4419[]={7821,0},
-dd4420[]={7823,0},
-dd4421[]={7825,0},
-dd4422[]={7827,0},
-dd4423[]={7829,0},
-dd4424[]={7841,0},
-dd4425[]={7843,0},
-dd4426[]={7845,0},
-dd4427[]={7847,0},
-dd4428[]={7849,0},
-dd4429[]={7851,0},
-dd4430[]={7853,0},
-dd4431[]={7855,0},
-dd4432[]={7857,0},
-dd4433[]={7859,0},
-dd4434[]={7861,0},
-dd4435[]={7863,0},
-dd4436[]={7865,0},
-dd4437[]={7867,0},
-dd4438[]={7869,0},
-dd4439[]={7871,0},
-dd4440[]={7873,0},
-dd4441[]={7875,0},
-dd4442[]={7877,0},
-dd4443[]={7881,0},
-dd4444[]={7883,0},
-dd4445[]={7885,0},
-dd4446[]={7887,0},
-dd4447[]={7889,0},
-dd4448[]={7891,0},
-dd4449[]={7893,0},
-dd4450[]={7895,0},
-dd4451[]={7899,0},
-dd4452[]={7901,0},
-dd4453[]={7903,0},
-dd4454[]={7905,0},
-dd4455[]={7907,0},
-dd4456[]={7909,0},
-dd4457[]={7911,0},
-dd4458[]={7913,0},
-dd4459[]={7915,0},
-dd4460[]={7917,0},
-dd4461[]={7919,0},
-dd4462[]={7921,0},
-dd4463[]={7923,0},
-dd4464[]={7925,0},
-dd4465[]={7927,0},
-dd4466[]={7929,0},
-dd4467[]={7936,0},
-dd4468[]={7937,0},
-dd4469[]={7938,0},
-dd4470[]={7939,0},
-dd4471[]={7940,0},
-dd4472[]={7941,0},
-dd4473[]={7942,0},
-dd4474[]={7943,0},
-dd4475[]={7952,0},
-dd4476[]={7953,0},
-dd4477[]={7954,0},
-dd4478[]={7955,0},
-dd4479[]={7956,0},
-dd4480[]={7957,0},
-dd4481[]={7968,0},
-dd4482[]={7969,0},
-dd4483[]={7970,0},
-dd4484[]={7971,0},
-dd4485[]={7972,0},
-dd4486[]={7973,0},
-dd4487[]={7974,0},
-dd4488[]={7975,0},
-dd4489[]={7984,0},
-dd4490[]={7985,0},
-dd4491[]={7986,0},
-dd4492[]={7987,0},
-dd4493[]={7988,0},
-dd4494[]={7989,0},
-dd4495[]={7990,0},
-dd4496[]={7991,0},
-dd4497[]={8000,0},
-dd4498[]={8001,0},
-dd4499[]={8002,0},
-dd4500[]={8003,0},
-dd4501[]={8004,0},
-dd4502[]={8005,0},
-dd4503[]={965,787,768,0},
-dd4504[]={965,787,769,0},
-dd4505[]={965,787,834,0},
-dd4506[]={8017,0},
-dd4507[]={8019,0},
-dd4508[]={8021,0},
-dd4509[]={8023,0},
-dd4510[]={8032,0},
-dd4511[]={8033,0},
-dd4512[]={8034,0},
-dd4513[]={8035,0},
-dd4514[]={8036,0},
-dd4515[]={8037,0},
-dd4516[]={8038,0},
-dd4517[]={8039,0},
-dd4518[]={7936,953,0},
-dd4519[]={7937,953,0},
-dd4520[]={7938,953,0},
-dd4521[]={7939,953,0},
-dd4522[]={7940,953,0},
-dd4523[]={7941,953,0},
-dd4524[]={7942,953,0},
-dd4525[]={7943,953,0},
-dd4526[]={7968,953,0},
-dd4527[]={7969,953,0},
-dd4528[]={7970,953,0},
-dd4529[]={7971,953,0},
-dd4530[]={7972,953,0},
-dd4531[]={7973,953,0},
-dd4532[]={7974,953,0},
-dd4533[]={7975,953,0},
-dd4534[]={8032,953,0},
-dd4535[]={8033,953,0},
-dd4536[]={8034,953,0},
-dd4537[]={8035,953,0},
-dd4538[]={8036,953,0},
-dd4539[]={8037,953,0},
-dd4540[]={8038,953,0},
-dd4541[]={8039,953,0},
-dd4542[]={8048,953,0},
-dd4543[]={945,953,0},
-dd4544[]={940,953,0},
-dd4545[]={945,834,953,0},
-dd4546[]={8112,0},
-dd4547[]={8113,0},
-dd4548[]={8048,0},
-dd4549[]={8049,0},
-dd4550[]={8052,953,0},
-dd4551[]={951,953,0},
-dd4552[]={942,953,0},
-dd4553[]={951,834,953,0},
-dd4554[]={8050,0},
-dd4555[]={8051,0},
-dd4556[]={8052,0},
-dd4557[]={8053,0},
-dd4558[]={953,776,768,0},
-dd4559[]={953,776,834,0},
-dd4560[]={8144,0},
-dd4561[]={8145,0},
-dd4562[]={8054,0},
-dd4563[]={8055,0},
-dd4564[]={965,776,768,0},
-dd4565[]={965,776,834,0},
-dd4566[]={8160,0},
-dd4567[]={8161,0},
-dd4568[]={8058,0},
-dd4569[]={8059,0},
-dd4570[]={8165,0},
-dd4571[]={8060,953,0},
-dd4572[]={969,953,0},
-dd4573[]={974,953,0},
-dd4574[]={969,834,953,0},
-dd4575[]={8056,0},
-dd4576[]={8057,0},
-dd4577[]={8060,0},
-dd4578[]={8061,0},
-dd4579[]={8526,0},
-dd4580[]={8560,0},
-dd4581[]={8561,0},
-dd4582[]={8562,0},
-dd4583[]={8563,0},
-dd4584[]={8564,0},
-dd4585[]={8565,0},
-dd4586[]={8566,0},
-dd4587[]={8567,0},
-dd4588[]={8568,0},
-dd4589[]={8569,0},
-dd4590[]={8570,0},
-dd4591[]={8571,0},
-dd4592[]={8572,0},
-dd4593[]={8573,0},
-dd4594[]={8574,0},
-dd4595[]={8575,0},
-dd4596[]={8580,0},
-dd4597[]={9424,0},
-dd4598[]={9425,0},
-dd4599[]={9426,0},
-dd4600[]={9427,0},
-dd4601[]={9428,0},
-dd4602[]={9429,0},
-dd4603[]={9430,0},
-dd4604[]={9431,0},
-dd4605[]={9432,0},
-dd4606[]={9433,0},
-dd4607[]={9434,0},
-dd4608[]={9435,0},
-dd4609[]={9436,0},
-dd4610[]={9437,0},
-dd4611[]={9438,0},
-dd4612[]={9439,0},
-dd4613[]={9440,0},
-dd4614[]={9441,0},
-dd4615[]={9442,0},
-dd4616[]={9443,0},
-dd4617[]={9444,0},
-dd4618[]={9445,0},
-dd4619[]={9446,0},
-dd4620[]={9447,0},
-dd4621[]={9448,0},
-dd4622[]={9449,0},
-dd4623[]={11312,0},
-dd4624[]={11313,0},
-dd4625[]={11314,0},
-dd4626[]={11315,0},
-dd4627[]={11316,0},
-dd4628[]={11317,0},
-dd4629[]={11318,0},
-dd4630[]={11319,0},
-dd4631[]={11320,0},
-dd4632[]={11321,0},
-dd4633[]={11322,0},
-dd4634[]={11323,0},
-dd4635[]={11324,0},
-dd4636[]={11325,0},
-dd4637[]={11326,0},
-dd4638[]={11327,0},
-dd4639[]={11328,0},
-dd4640[]={11329,0},
-dd4641[]={11330,0},
-dd4642[]={11331,0},
-dd4643[]={11332,0},
-dd4644[]={11333,0},
-dd4645[]={11334,0},
-dd4646[]={11335,0},
-dd4647[]={11336,0},
-dd4648[]={11337,0},
-dd4649[]={11338,0},
-dd4650[]={11339,0},
-dd4651[]={11340,0},
-dd4652[]={11341,0},
-dd4653[]={11342,0},
-dd4654[]={11343,0},
-dd4655[]={11344,0},
-dd4656[]={11345,0},
-dd4657[]={11346,0},
-dd4658[]={11347,0},
-dd4659[]={11348,0},
-dd4660[]={11349,0},
-dd4661[]={11350,0},
-dd4662[]={11351,0},
-dd4663[]={11352,0},
-dd4664[]={11353,0},
-dd4665[]={11354,0},
-dd4666[]={11355,0},
-dd4667[]={11356,0},
-dd4668[]={11357,0},
-dd4669[]={11358,0},
-dd4670[]={11361,0},
-dd4671[]={619,0},
-dd4672[]={7549,0},
-dd4673[]={637,0},
-dd4674[]={11368,0},
-dd4675[]={11370,0},
-dd4676[]={11372,0},
-dd4677[]={11382,0},
-dd4678[]={11393,0},
-dd4679[]={11395,0},
-dd4680[]={11397,0},
-dd4681[]={11399,0},
-dd4682[]={11401,0},
-dd4683[]={11403,0},
-dd4684[]={11405,0},
-dd4685[]={11407,0},
-dd4686[]={11409,0},
-dd4687[]={11411,0},
-dd4688[]={11413,0},
-dd4689[]={11415,0},
-dd4690[]={11417,0},
-dd4691[]={11419,0},
-dd4692[]={11421,0},
-dd4693[]={11423,0},
-dd4694[]={11425,0},
-dd4695[]={11427,0},
-dd4696[]={11429,0},
-dd4697[]={11431,0},
-dd4698[]={11433,0},
-dd4699[]={11435,0},
-dd4700[]={11437,0},
-dd4701[]={11439,0},
-dd4702[]={11441,0},
-dd4703[]={11443,0},
-dd4704[]={11445,0},
-dd4705[]={11447,0},
-dd4706[]={11449,0},
-dd4707[]={11451,0},
-dd4708[]={11453,0},
-dd4709[]={11455,0},
-dd4710[]={11457,0},
-dd4711[]={11459,0},
-dd4712[]={11461,0},
-dd4713[]={11463,0},
-dd4714[]={11465,0},
-dd4715[]={11467,0},
-dd4716[]={11469,0},
-dd4717[]={11471,0},
-dd4718[]={11473,0},
-dd4719[]={11475,0},
-dd4720[]={11477,0},
-dd4721[]={11479,0},
-dd4722[]={11481,0},
-dd4723[]={11483,0},
-dd4724[]={11485,0},
-dd4725[]={11487,0},
-dd4726[]={11489,0},
-dd4727[]={11491,0},
-dd4728[]={65345,0},
-dd4729[]={65346,0},
-dd4730[]={65347,0},
-dd4731[]={65348,0},
-dd4732[]={65349,0},
-dd4733[]={65350,0},
-dd4734[]={65351,0},
-dd4735[]={65352,0},
-dd4736[]={65353,0},
-dd4737[]={65354,0},
-dd4738[]={65355,0},
-dd4739[]={65356,0},
-dd4740[]={65357,0},
-dd4741[]={65358,0},
-dd4742[]={65359,0},
-dd4743[]={65360,0},
-dd4744[]={65361,0},
-dd4745[]={65362,0},
-dd4746[]={65363,0},
-dd4747[]={65364,0},
-dd4748[]={65365,0},
-dd4749[]={65366,0},
-dd4750[]={65367,0},
-dd4751[]={65368,0},
-dd4752[]={65369,0},
-dd4753[]={65370,0},
-dd4754[]={66600,0},
-dd4755[]={66601,0},
-dd4756[]={66602,0},
-dd4757[]={66603,0},
-dd4758[]={66604,0},
-dd4759[]={66605,0},
-dd4760[]={66606,0},
-dd4761[]={66607,0},
-dd4762[]={66608,0},
-dd4763[]={66609,0},
-dd4764[]={66610,0},
-dd4765[]={66611,0},
-dd4766[]={66612,0},
-dd4767[]={66613,0},
-dd4768[]={66614,0},
-dd4769[]={66615,0},
-dd4770[]={66616,0},
-dd4771[]={66617,0},
-dd4772[]={66618,0},
-dd4773[]={66619,0},
-dd4774[]={66620,0},
-dd4775[]={66621,0},
-dd4776[]={66622,0},
-dd4777[]={66623,0},
-dd4778[]={66624,0},
-dd4779[]={66625,0},
-dd4780[]={66626,0},
-dd4781[]={66627,0},
-dd4782[]={66628,0},
-dd4783[]={66629,0},
-dd4784[]={66630,0},
-dd4785[]={66631,0},
-dd4786[]={66632,0},
-dd4787[]={66633,0},
-dd4788[]={66634,0},
-dd4789[]={66635,0},
-dd4790[]={66636,0},
-dd4791[]={66637,0},
-dd4792[]={66638,0},
-dd4793[]={66639,0};
+dd2130[]={42863,0},
+dd2131[]={35912,0},
+dd2132[]={26356,0},
+dd2133[]={36040,0},
+dd2134[]={28369,0},
+dd2135[]={20018,0},
+dd2136[]={21477,0},
+dd2137[]={22865,0},
+dd2138[]={21895,0},
+dd2139[]={22856,0},
+dd2140[]={25078,0},
+dd2141[]={30313,0},
+dd2142[]={32645,0},
+dd2143[]={34367,0},
+dd2144[]={34746,0},
+dd2145[]={35064,0},
+dd2146[]={37007,0},
+dd2147[]={27138,0},
+dd2148[]={27931,0},
+dd2149[]={28889,0},
+dd2150[]={29662,0},
+dd2151[]={33853,0},
+dd2152[]={37226,0},
+dd2153[]={39409,0},
+dd2154[]={20098,0},
+dd2155[]={21365,0},
+dd2156[]={27396,0},
+dd2157[]={29211,0},
+dd2158[]={34349,0},
+dd2159[]={40478,0},
+dd2160[]={23888,0},
+dd2161[]={28651,0},
+dd2162[]={34253,0},
+dd2163[]={35172,0},
+dd2164[]={25289,0},
+dd2165[]={33240,0},
+dd2166[]={34847,0},
+dd2167[]={24266,0},
+dd2168[]={26391,0},
+dd2169[]={28010,0},
+dd2170[]={29436,0},
+dd2171[]={37070,0},
+dd2172[]={20358,0},
+dd2173[]={20919,0},
+dd2174[]={21214,0},
+dd2175[]={25796,0},
+dd2176[]={27347,0},
+dd2177[]={29200,0},
+dd2178[]={30439,0},
+dd2179[]={34310,0},
+dd2180[]={34396,0},
+dd2181[]={36335,0},
+dd2182[]={38706,0},
+dd2183[]={39791,0},
+dd2184[]={40442,0},
+dd2185[]={30860,0},
+dd2186[]={31103,0},
+dd2187[]={32160,0},
+dd2188[]={33737,0},
+dd2189[]={37636,0},
+dd2190[]={35542,0},
+dd2191[]={22751,0},
+dd2192[]={24324,0},
+dd2193[]={31840,0},
+dd2194[]={32894,0},
+dd2195[]={29282,0},
+dd2196[]={30922,0},
+dd2197[]={36034,0},
+dd2198[]={38647,0},
+dd2199[]={22744,0},
+dd2200[]={23650,0},
+dd2201[]={27155,0},
+dd2202[]={28122,0},
+dd2203[]={28431,0},
+dd2204[]={32047,0},
+dd2205[]={32311,0},
+dd2206[]={38475,0},
+dd2207[]={21202,0},
+dd2208[]={32907,0},
+dd2209[]={20956,0},
+dd2210[]={20940,0},
+dd2211[]={31260,0},
+dd2212[]={32190,0},
+dd2213[]={33777,0},
+dd2214[]={38517,0},
+dd2215[]={35712,0},
+dd2216[]={25295,0},
+dd2217[]={35582,0},
+dd2218[]={20025,0},
+dd2219[]={23527,0},
+dd2220[]={24594,0},
+dd2221[]={29575,0},
+dd2222[]={30064,0},
+dd2223[]={21271,0},
+dd2224[]={30971,0},
+dd2225[]={20415,0},
+dd2226[]={24489,0},
+dd2227[]={19981,0},
+dd2228[]={27852,0},
+dd2229[]={25976,0},
+dd2230[]={32034,0},
+dd2231[]={21443,0},
+dd2232[]={22622,0},
+dd2233[]={30465,0},
+dd2234[]={33865,0},
+dd2235[]={35498,0},
+dd2236[]={27578,0},
+dd2237[]={27784,0},
+dd2238[]={25342,0},
+dd2239[]={33509,0},
+dd2240[]={25504,0},
+dd2241[]={30053,0},
+dd2242[]={20142,0},
+dd2243[]={20841,0},
+dd2244[]={20937,0},
+dd2245[]={26753,0},
+dd2246[]={31975,0},
+dd2247[]={33391,0},
+dd2248[]={35538,0},
+dd2249[]={37327,0},
+dd2250[]={21237,0},
+dd2251[]={21570,0},
+dd2252[]={24300,0},
+dd2253[]={26053,0},
+dd2254[]={28670,0},
+dd2255[]={31018,0},
+dd2256[]={38317,0},
+dd2257[]={39530,0},
+dd2258[]={40599,0},
+dd2259[]={40654,0},
+dd2260[]={26310,0},
+dd2261[]={27511,0},
+dd2262[]={36706,0},
+dd2263[]={24180,0},
+dd2264[]={24976,0},
+dd2265[]={25088,0},
+dd2266[]={25754,0},
+dd2267[]={28451,0},
+dd2268[]={29001,0},
+dd2269[]={29833,0},
+dd2270[]={31178,0},
+dd2271[]={32244,0},
+dd2272[]={32879,0},
+dd2273[]={36646,0},
+dd2274[]={34030,0},
+dd2275[]={36899,0},
+dd2276[]={37706,0},
+dd2277[]={21015,0},
+dd2278[]={21155,0},
+dd2279[]={21693,0},
+dd2280[]={28872,0},
+dd2281[]={35010,0},
+dd2282[]={24265,0},
+dd2283[]={24565,0},
+dd2284[]={25467,0},
+dd2285[]={27566,0},
+dd2286[]={31806,0},
+dd2287[]={29557,0},
+dd2288[]={20196,0},
+dd2289[]={22265,0},
+dd2290[]={23994,0},
+dd2291[]={24604,0},
+dd2292[]={29618,0},
+dd2293[]={29801,0},
+dd2294[]={32666,0},
+dd2295[]={32838,0},
+dd2296[]={37428,0},
+dd2297[]={38646,0},
+dd2298[]={38728,0},
+dd2299[]={38936,0},
+dd2300[]={20363,0},
+dd2301[]={31150,0},
+dd2302[]={37300,0},
+dd2303[]={38584,0},
+dd2304[]={24801,0},
+dd2305[]={20102,0},
+dd2306[]={20698,0},
+dd2307[]={23534,0},
+dd2308[]={23615,0},
+dd2309[]={26009,0},
+dd2310[]={29134,0},
+dd2311[]={30274,0},
+dd2312[]={34044,0},
+dd2313[]={36988,0},
+dd2314[]={26248,0},
+dd2315[]={38446,0},
+dd2316[]={21129,0},
+dd2317[]={26491,0},
+dd2318[]={26611,0},
+dd2319[]={27969,0},
+dd2320[]={28316,0},
+dd2321[]={29705,0},
+dd2322[]={30041,0},
+dd2323[]={30827,0},
+dd2324[]={32016,0},
+dd2325[]={39006,0},
+dd2326[]={25134,0},
+dd2327[]={38520,0},
+dd2328[]={20523,0},
+dd2329[]={23833,0},
+dd2330[]={28138,0},
+dd2331[]={36650,0},
+dd2332[]={24459,0},
+dd2333[]={24900,0},
+dd2334[]={26647,0},
+dd2335[]={38534,0},
+dd2336[]={21033,0},
+dd2337[]={21519,0},
+dd2338[]={23653,0},
+dd2339[]={26131,0},
+dd2340[]={26446,0},
+dd2341[]={26792,0},
+dd2342[]={27877,0},
+dd2343[]={29702,0},
+dd2344[]={30178,0},
+dd2345[]={32633,0},
+dd2346[]={35023,0},
+dd2347[]={35041,0},
+dd2348[]={38626,0},
+dd2349[]={21311,0},
+dd2350[]={28346,0},
+dd2351[]={21533,0},
+dd2352[]={29136,0},
+dd2353[]={29848,0},
+dd2354[]={34298,0},
+dd2355[]={38563,0},
+dd2356[]={40023,0},
+dd2357[]={40607,0},
+dd2358[]={26519,0},
+dd2359[]={28107,0},
+dd2360[]={33256,0},
+dd2361[]={31520,0},
+dd2362[]={31890,0},
+dd2363[]={29376,0},
+dd2364[]={28825,0},
+dd2365[]={35672,0},
+dd2366[]={20160,0},
+dd2367[]={33590,0},
+dd2368[]={21050,0},
+dd2369[]={20999,0},
+dd2370[]={24230,0},
+dd2371[]={25299,0},
+dd2372[]={31958,0},
+dd2373[]={23429,0},
+dd2374[]={27934,0},
+dd2375[]={26292,0},
+dd2376[]={36667,0},
+dd2377[]={38477,0},
+dd2378[]={24275,0},
+dd2379[]={20800,0},
+dd2380[]={21952,0},
+dd2381[]={22618,0},
+dd2382[]={26228,0},
+dd2383[]={20958,0},
+dd2384[]={29482,0},
+dd2385[]={30410,0},
+dd2386[]={31036,0},
+dd2387[]={31070,0},
+dd2388[]={31077,0},
+dd2389[]={31119,0},
+dd2390[]={38742,0},
+dd2391[]={31934,0},
+dd2392[]={34322,0},
+dd2393[]={35576,0},
+dd2394[]={36920,0},
+dd2395[]={37117,0},
+dd2396[]={39151,0},
+dd2397[]={39164,0},
+dd2398[]={39208,0},
+dd2399[]={40372,0},
+dd2400[]={20398,0},
+dd2401[]={20711,0},
+dd2402[]={20813,0},
+dd2403[]={21193,0},
+dd2404[]={21220,0},
+dd2405[]={21329,0},
+dd2406[]={21917,0},
+dd2407[]={22022,0},
+dd2408[]={22120,0},
+dd2409[]={22592,0},
+dd2410[]={22696,0},
+dd2411[]={23652,0},
+dd2412[]={24724,0},
+dd2413[]={24936,0},
+dd2414[]={24974,0},
+dd2415[]={25074,0},
+dd2416[]={25935,0},
+dd2417[]={26082,0},
+dd2418[]={26257,0},
+dd2419[]={26757,0},
+dd2420[]={28023,0},
+dd2421[]={28186,0},
+dd2422[]={28450,0},
+dd2423[]={29038,0},
+dd2424[]={29227,0},
+dd2425[]={29730,0},
+dd2426[]={30865,0},
+dd2427[]={31049,0},
+dd2428[]={31048,0},
+dd2429[]={31056,0},
+dd2430[]={31062,0},
+dd2431[]={31117,0},
+dd2432[]={31118,0},
+dd2433[]={31296,0},
+dd2434[]={31361,0},
+dd2435[]={31680,0},
+dd2436[]={32265,0},
+dd2437[]={32321,0},
+dd2438[]={32626,0},
+dd2439[]={32773,0},
+dd2440[]={33261,0},
+dd2441[]={33401,0},
+dd2442[]={33879,0},
+dd2443[]={35088,0},
+dd2444[]={35222,0},
+dd2445[]={35585,0},
+dd2446[]={35641,0},
+dd2447[]={36051,0},
+dd2448[]={36104,0},
+dd2449[]={36790,0},
+dd2450[]={38627,0},
+dd2451[]={38911,0},
+dd2452[]={38971,0},
+dd2453[]={20006,0},
+dd2454[]={20917,0},
+dd2455[]={20840,0},
+dd2456[]={20352,0},
+dd2457[]={20805,0},
+dd2458[]={20864,0},
+dd2459[]={21191,0},
+dd2460[]={21242,0},
+dd2461[]={21845,0},
+dd2462[]={21913,0},
+dd2463[]={21986,0},
+dd2464[]={22707,0},
+dd2465[]={22852,0},
+dd2466[]={22868,0},
+dd2467[]={23138,0},
+dd2468[]={23336,0},
+dd2469[]={24274,0},
+dd2470[]={24281,0},
+dd2471[]={24425,0},
+dd2472[]={24493,0},
+dd2473[]={24792,0},
+dd2474[]={24910,0},
+dd2475[]={24840,0},
+dd2476[]={24928,0},
+dd2477[]={25140,0},
+dd2478[]={25540,0},
+dd2479[]={25628,0},
+dd2480[]={25682,0},
+dd2481[]={25942,0},
+dd2482[]={26395,0},
+dd2483[]={26454,0},
+dd2484[]={28379,0},
+dd2485[]={28363,0},
+dd2486[]={28702,0},
+dd2487[]={30631,0},
+dd2488[]={29237,0},
+dd2489[]={29359,0},
+dd2490[]={29809,0},
+dd2491[]={29958,0},
+dd2492[]={30011,0},
+dd2493[]={30237,0},
+dd2494[]={30239,0},
+dd2495[]={30427,0},
+dd2496[]={30452,0},
+dd2497[]={30538,0},
+dd2498[]={30528,0},
+dd2499[]={30924,0},
+dd2500[]={31409,0},
+dd2501[]={31867,0},
+dd2502[]={32091,0},
+dd2503[]={32574,0},
+dd2504[]={33618,0},
+dd2505[]={33775,0},
+dd2506[]={34681,0},
+dd2507[]={35137,0},
+dd2508[]={35206,0},
+dd2509[]={35519,0},
+dd2510[]={35531,0},
+dd2511[]={35565,0},
+dd2512[]={35722,0},
+dd2513[]={36664,0},
+dd2514[]={36978,0},
+dd2515[]={37273,0},
+dd2516[]={37494,0},
+dd2517[]={38524,0},
+dd2518[]={38875,0},
+dd2519[]={38923,0},
+dd2520[]={39698,0},
+dd2521[]={141386,0},
+dd2522[]={141380,0},
+dd2523[]={144341,0},
+dd2524[]={15261,0},
+dd2525[]={16408,0},
+dd2526[]={16441,0},
+dd2527[]={152137,0},
+dd2528[]={154832,0},
+dd2529[]={163539,0},
+dd2530[]={40771,0},
+dd2531[]={40846,0},
+dd2532[]={102,102,0},
+dd2533[]={102,105,0},
+dd2534[]={102,108,0},
+dd2535[]={102,102,105,0},
+dd2536[]={102,102,108,0},
+dd2537[]={383,116,0},
+dd2538[]={115,116,0},
+dd2539[]={1396,1398,0},
+dd2540[]={1396,1381,0},
+dd2541[]={1396,1387,0},
+dd2542[]={1406,1398,0},
+dd2543[]={1396,1389,0},
+dd2544[]={1497,1460,0},
+dd2545[]={1522,1463,0},
+dd2546[]={1506,0},
+dd2547[]={1492,0},
+dd2548[]={1499,0},
+dd2549[]={1500,0},
+dd2550[]={1501,0},
+dd2551[]={1512,0},
+dd2552[]={1514,0},
+dd2553[]={1513,1473,0},
+dd2554[]={1513,1474,0},
+dd2555[]={64329,1473,0},
+dd2556[]={64329,1474,0},
+dd2557[]={1488,1463,0},
+dd2558[]={1488,1464,0},
+dd2559[]={1488,1468,0},
+dd2560[]={1489,1468,0},
+dd2561[]={1490,1468,0},
+dd2562[]={1491,1468,0},
+dd2563[]={1492,1468,0},
+dd2564[]={1493,1468,0},
+dd2565[]={1494,1468,0},
+dd2566[]={1496,1468,0},
+dd2567[]={1497,1468,0},
+dd2568[]={1498,1468,0},
+dd2569[]={1499,1468,0},
+dd2570[]={1500,1468,0},
+dd2571[]={1502,1468,0},
+dd2572[]={1504,1468,0},
+dd2573[]={1505,1468,0},
+dd2574[]={1507,1468,0},
+dd2575[]={1508,1468,0},
+dd2576[]={1510,1468,0},
+dd2577[]={1511,1468,0},
+dd2578[]={1512,1468,0},
+dd2579[]={1513,1468,0},
+dd2580[]={1514,1468,0},
+dd2581[]={1493,1465,0},
+dd2582[]={1489,1471,0},
+dd2583[]={1499,1471,0},
+dd2584[]={1508,1471,0},
+dd2585[]={1488,1500,0},
+dd2586[]={1649,0},
+dd2587[]={1659,0},
+dd2588[]={1662,0},
+dd2589[]={1664,0},
+dd2590[]={1658,0},
+dd2591[]={1663,0},
+dd2592[]={1657,0},
+dd2593[]={1700,0},
+dd2594[]={1702,0},
+dd2595[]={1668,0},
+dd2596[]={1667,0},
+dd2597[]={1670,0},
+dd2598[]={1671,0},
+dd2599[]={1677,0},
+dd2600[]={1676,0},
+dd2601[]={1678,0},
+dd2602[]={1672,0},
+dd2603[]={1688,0},
+dd2604[]={1681,0},
+dd2605[]={1705,0},
+dd2606[]={1711,0},
+dd2607[]={1715,0},
+dd2608[]={1713,0},
+dd2609[]={1722,0},
+dd2610[]={1723,0},
+dd2611[]={1728,0},
+dd2612[]={1729,0},
+dd2613[]={1726,0},
+dd2614[]={1746,0},
+dd2615[]={1747,0},
+dd2616[]={1709,0},
+dd2617[]={1735,0},
+dd2618[]={1734,0},
+dd2619[]={1736,0},
+dd2620[]={1655,0},
+dd2621[]={1739,0},
+dd2622[]={1733,0},
+dd2623[]={1737,0},
+dd2624[]={1744,0},
+dd2625[]={1609,0},
+dd2626[]={1574,1575,0},
+dd2627[]={1574,1749,0},
+dd2628[]={1574,1608,0},
+dd2629[]={1574,1735,0},
+dd2630[]={1574,1734,0},
+dd2631[]={1574,1736,0},
+dd2632[]={1574,1744,0},
+dd2633[]={1574,1609,0},
+dd2634[]={1740,0},
+dd2635[]={1574,1580,0},
+dd2636[]={1574,1581,0},
+dd2637[]={1574,1605,0},
+dd2638[]={1574,1610,0},
+dd2639[]={1576,1580,0},
+dd2640[]={1576,1581,0},
+dd2641[]={1576,1582,0},
+dd2642[]={1576,1605,0},
+dd2643[]={1576,1609,0},
+dd2644[]={1576,1610,0},
+dd2645[]={1578,1580,0},
+dd2646[]={1578,1581,0},
+dd2647[]={1578,1582,0},
+dd2648[]={1578,1605,0},
+dd2649[]={1578,1609,0},
+dd2650[]={1578,1610,0},
+dd2651[]={1579,1580,0},
+dd2652[]={1579,1605,0},
+dd2653[]={1579,1609,0},
+dd2654[]={1579,1610,0},
+dd2655[]={1580,1581,0},
+dd2656[]={1580,1605,0},
+dd2657[]={1581,1580,0},
+dd2658[]={1581,1605,0},
+dd2659[]={1582,1580,0},
+dd2660[]={1582,1581,0},
+dd2661[]={1582,1605,0},
+dd2662[]={1587,1580,0},
+dd2663[]={1587,1581,0},
+dd2664[]={1587,1582,0},
+dd2665[]={1587,1605,0},
+dd2666[]={1589,1581,0},
+dd2667[]={1589,1605,0},
+dd2668[]={1590,1580,0},
+dd2669[]={1590,1581,0},
+dd2670[]={1590,1582,0},
+dd2671[]={1590,1605,0},
+dd2672[]={1591,1581,0},
+dd2673[]={1591,1605,0},
+dd2674[]={1592,1605,0},
+dd2675[]={1593,1580,0},
+dd2676[]={1593,1605,0},
+dd2677[]={1594,1580,0},
+dd2678[]={1594,1605,0},
+dd2679[]={1601,1580,0},
+dd2680[]={1601,1581,0},
+dd2681[]={1601,1582,0},
+dd2682[]={1601,1605,0},
+dd2683[]={1601,1609,0},
+dd2684[]={1601,1610,0},
+dd2685[]={1602,1581,0},
+dd2686[]={1602,1605,0},
+dd2687[]={1602,1609,0},
+dd2688[]={1602,1610,0},
+dd2689[]={1603,1575,0},
+dd2690[]={1603,1580,0},
+dd2691[]={1603,1581,0},
+dd2692[]={1603,1582,0},
+dd2693[]={1603,1604,0},
+dd2694[]={1603,1605,0},
+dd2695[]={1603,1609,0},
+dd2696[]={1603,1610,0},
+dd2697[]={1604,1580,0},
+dd2698[]={1604,1581,0},
+dd2699[]={1604,1582,0},
+dd2700[]={1604,1605,0},
+dd2701[]={1604,1609,0},
+dd2702[]={1604,1610,0},
+dd2703[]={1605,1580,0},
+dd2704[]={1605,1581,0},
+dd2705[]={1605,1582,0},
+dd2706[]={1605,1605,0},
+dd2707[]={1605,1609,0},
+dd2708[]={1605,1610,0},
+dd2709[]={1606,1580,0},
+dd2710[]={1606,1581,0},
+dd2711[]={1606,1582,0},
+dd2712[]={1606,1605,0},
+dd2713[]={1606,1609,0},
+dd2714[]={1606,1610,0},
+dd2715[]={1607,1580,0},
+dd2716[]={1607,1605,0},
+dd2717[]={1607,1609,0},
+dd2718[]={1607,1610,0},
+dd2719[]={1610,1580,0},
+dd2720[]={1610,1581,0},
+dd2721[]={1610,1582,0},
+dd2722[]={1610,1605,0},
+dd2723[]={1610,1609,0},
+dd2724[]={1610,1610,0},
+dd2725[]={1584,1648,0},
+dd2726[]={1585,1648,0},
+dd2727[]={1609,1648,0},
+dd2728[]={32,1612,1617,0},
+dd2729[]={32,1613,1617,0},
+dd2730[]={32,1614,1617,0},
+dd2731[]={32,1615,1617,0},
+dd2732[]={32,1616,1617,0},
+dd2733[]={32,1617,1648,0},
+dd2734[]={1574,1585,0},
+dd2735[]={1574,1586,0},
+dd2736[]={1574,1606,0},
+dd2737[]={1576,1585,0},
+dd2738[]={1576,1586,0},
+dd2739[]={1576,1606,0},
+dd2740[]={1578,1585,0},
+dd2741[]={1578,1586,0},
+dd2742[]={1578,1606,0},
+dd2743[]={1579,1585,0},
+dd2744[]={1579,1586,0},
+dd2745[]={1579,1606,0},
+dd2746[]={1605,1575,0},
+dd2747[]={1606,1585,0},
+dd2748[]={1606,1586,0},
+dd2749[]={1606,1606,0},
+dd2750[]={1610,1585,0},
+dd2751[]={1610,1586,0},
+dd2752[]={1610,1606,0},
+dd2753[]={1574,1582,0},
+dd2754[]={1574,1607,0},
+dd2755[]={1576,1607,0},
+dd2756[]={1578,1607,0},
+dd2757[]={1589,1582,0},
+dd2758[]={1604,1607,0},
+dd2759[]={1606,1607,0},
+dd2760[]={1607,1648,0},
+dd2761[]={1610,1607,0},
+dd2762[]={1579,1607,0},
+dd2763[]={1587,1607,0},
+dd2764[]={1588,1605,0},
+dd2765[]={1588,1607,0},
+dd2766[]={1600,1614,1617,0},
+dd2767[]={1600,1615,1617,0},
+dd2768[]={1600,1616,1617,0},
+dd2769[]={1591,1609,0},
+dd2770[]={1591,1610,0},
+dd2771[]={1593,1609,0},
+dd2772[]={1593,1610,0},
+dd2773[]={1594,1609,0},
+dd2774[]={1594,1610,0},
+dd2775[]={1587,1609,0},
+dd2776[]={1587,1610,0},
+dd2777[]={1588,1609,0},
+dd2778[]={1588,1610,0},
+dd2779[]={1581,1609,0},
+dd2780[]={1581,1610,0},
+dd2781[]={1580,1609,0},
+dd2782[]={1580,1610,0},
+dd2783[]={1582,1609,0},
+dd2784[]={1582,1610,0},
+dd2785[]={1589,1609,0},
+dd2786[]={1589,1610,0},
+dd2787[]={1590,1609,0},
+dd2788[]={1590,1610,0},
+dd2789[]={1588,1580,0},
+dd2790[]={1588,1581,0},
+dd2791[]={1588,1582,0},
+dd2792[]={1588,1585,0},
+dd2793[]={1587,1585,0},
+dd2794[]={1589,1585,0},
+dd2795[]={1590,1585,0},
+dd2796[]={1575,1611,0},
+dd2797[]={1578,1580,1605,0},
+dd2798[]={1578,1581,1580,0},
+dd2799[]={1578,1581,1605,0},
+dd2800[]={1578,1582,1605,0},
+dd2801[]={1578,1605,1580,0},
+dd2802[]={1578,1605,1581,0},
+dd2803[]={1578,1605,1582,0},
+dd2804[]={1580,1605,1581,0},
+dd2805[]={1581,1605,1610,0},
+dd2806[]={1581,1605,1609,0},
+dd2807[]={1587,1581,1580,0},
+dd2808[]={1587,1580,1581,0},
+dd2809[]={1587,1580,1609,0},
+dd2810[]={1587,1605,1581,0},
+dd2811[]={1587,1605,1580,0},
+dd2812[]={1587,1605,1605,0},
+dd2813[]={1589,1581,1581,0},
+dd2814[]={1589,1605,1605,0},
+dd2815[]={1588,1581,1605,0},
+dd2816[]={1588,1580,1610,0},
+dd2817[]={1588,1605,1582,0},
+dd2818[]={1588,1605,1605,0},
+dd2819[]={1590,1581,1609,0},
+dd2820[]={1590,1582,1605,0},
+dd2821[]={1591,1605,1581,0},
+dd2822[]={1591,1605,1605,0},
+dd2823[]={1591,1605,1610,0},
+dd2824[]={1593,1580,1605,0},
+dd2825[]={1593,1605,1605,0},
+dd2826[]={1593,1605,1609,0},
+dd2827[]={1594,1605,1605,0},
+dd2828[]={1594,1605,1610,0},
+dd2829[]={1594,1605,1609,0},
+dd2830[]={1601,1582,1605,0},
+dd2831[]={1602,1605,1581,0},
+dd2832[]={1602,1605,1605,0},
+dd2833[]={1604,1581,1605,0},
+dd2834[]={1604,1581,1610,0},
+dd2835[]={1604,1581,1609,0},
+dd2836[]={1604,1580,1580,0},
+dd2837[]={1604,1582,1605,0},
+dd2838[]={1604,1605,1581,0},
+dd2839[]={1605,1581,1580,0},
+dd2840[]={1605,1581,1605,0},
+dd2841[]={1605,1581,1610,0},
+dd2842[]={1605,1580,1581,0},
+dd2843[]={1605,1580,1605,0},
+dd2844[]={1605,1582,1580,0},
+dd2845[]={1605,1582,1605,0},
+dd2846[]={1605,1580,1582,0},
+dd2847[]={1607,1605,1580,0},
+dd2848[]={1607,1605,1605,0},
+dd2849[]={1606,1581,1605,0},
+dd2850[]={1606,1581,1609,0},
+dd2851[]={1606,1580,1605,0},
+dd2852[]={1606,1580,1609,0},
+dd2853[]={1606,1605,1610,0},
+dd2854[]={1606,1605,1609,0},
+dd2855[]={1610,1605,1605,0},
+dd2856[]={1576,1582,1610,0},
+dd2857[]={1578,1580,1610,0},
+dd2858[]={1578,1580,1609,0},
+dd2859[]={1578,1582,1610,0},
+dd2860[]={1578,1582,1609,0},
+dd2861[]={1578,1605,1610,0},
+dd2862[]={1578,1605,1609,0},
+dd2863[]={1580,1605,1610,0},
+dd2864[]={1580,1581,1609,0},
+dd2865[]={1580,1605,1609,0},
+dd2866[]={1587,1582,1609,0},
+dd2867[]={1589,1581,1610,0},
+dd2868[]={1588,1581,1610,0},
+dd2869[]={1590,1581,1610,0},
+dd2870[]={1604,1580,1610,0},
+dd2871[]={1604,1605,1610,0},
+dd2872[]={1610,1581,1610,0},
+dd2873[]={1610,1580,1610,0},
+dd2874[]={1610,1605,1610,0},
+dd2875[]={1605,1605,1610,0},
+dd2876[]={1602,1605,1610,0},
+dd2877[]={1606,1581,1610,0},
+dd2878[]={1593,1605,1610,0},
+dd2879[]={1603,1605,1610,0},
+dd2880[]={1606,1580,1581,0},
+dd2881[]={1605,1582,1610,0},
+dd2882[]={1604,1580,1605,0},
+dd2883[]={1603,1605,1605,0},
+dd2884[]={1580,1581,1610,0},
+dd2885[]={1581,1580,1610,0},
+dd2886[]={1605,1580,1610,0},
+dd2887[]={1601,1605,1610,0},
+dd2888[]={1576,1581,1610,0},
+dd2889[]={1587,1582,1610,0},
+dd2890[]={1606,1580,1610,0},
+dd2891[]={1589,1604,1746,0},
+dd2892[]={1602,1604,1746,0},
+dd2893[]={1575,1604,1604,1607,0},
+dd2894[]={1575,1603,1576,1585,0},
+dd2895[]={1605,1581,1605,1583,0},
+dd2896[]={1589,1604,1593,1605,0},
+dd2897[]={1585,1587,1608,1604,0},
+dd2898[]={1593,1604,1610,1607,0},
+dd2899[]={1608,1587,1604,1605,0},
+dd2900[]={1589,1604,1609,0},
+dd2901[]={1589,1604,1609,32,1575,1604,1604,1607,32,1593,1604,1610,1607,32,1608,1587,1604,1605,0},
+dd2902[]={1580,1604,32,1580,1604,1575,1604,1607,0},
+dd2903[]={1585,1740,1575,1604,0},
+dd2904[]={44,0},
+dd2905[]={12289,0},
+dd2906[]={12290,0},
+dd2907[]={58,0},
+dd2908[]={33,0},
+dd2909[]={63,0},
+dd2910[]={12310,0},
+dd2911[]={12311,0},
+dd2912[]={8230,0},
+dd2913[]={8229,0},
+dd2914[]={8212,0},
+dd2915[]={8211,0},
+dd2916[]={95,0},
+dd2917[]={123,0},
+dd2918[]={125,0},
+dd2919[]={12308,0},
+dd2920[]={12309,0},
+dd2921[]={12304,0},
+dd2922[]={12305,0},
+dd2923[]={12298,0},
+dd2924[]={12299,0},
+dd2925[]={12300,0},
+dd2926[]={12301,0},
+dd2927[]={12302,0},
+dd2928[]={12303,0},
+dd2929[]={91,0},
+dd2930[]={93,0},
+dd2931[]={8254,0},
+dd2932[]={35,0},
+dd2933[]={38,0},
+dd2934[]={42,0},
+dd2935[]={45,0},
+dd2936[]={60,0},
+dd2937[]={62,0},
+dd2938[]={92,0},
+dd2939[]={36,0},
+dd2940[]={37,0},
+dd2941[]={64,0},
+dd2942[]={32,1611,0},
+dd2943[]={1600,1611,0},
+dd2944[]={32,1612,0},
+dd2945[]={32,1613,0},
+dd2946[]={32,1614,0},
+dd2947[]={1600,1614,0},
+dd2948[]={32,1615,0},
+dd2949[]={1600,1615,0},
+dd2950[]={32,1616,0},
+dd2951[]={1600,1616,0},
+dd2952[]={32,1617,0},
+dd2953[]={1600,1617,0},
+dd2954[]={32,1618,0},
+dd2955[]={1600,1618,0},
+dd2956[]={1569,0},
+dd2957[]={1570,0},
+dd2958[]={1571,0},
+dd2959[]={1572,0},
+dd2960[]={1573,0},
+dd2961[]={1574,0},
+dd2962[]={1575,0},
+dd2963[]={1576,0},
+dd2964[]={1577,0},
+dd2965[]={1578,0},
+dd2966[]={1579,0},
+dd2967[]={1580,0},
+dd2968[]={1581,0},
+dd2969[]={1582,0},
+dd2970[]={1583,0},
+dd2971[]={1584,0},
+dd2972[]={1585,0},
+dd2973[]={1586,0},
+dd2974[]={1587,0},
+dd2975[]={1588,0},
+dd2976[]={1589,0},
+dd2977[]={1590,0},
+dd2978[]={1591,0},
+dd2979[]={1592,0},
+dd2980[]={1593,0},
+dd2981[]={1594,0},
+dd2982[]={1601,0},
+dd2983[]={1602,0},
+dd2984[]={1603,0},
+dd2985[]={1604,0},
+dd2986[]={1605,0},
+dd2987[]={1606,0},
+dd2988[]={1607,0},
+dd2989[]={1608,0},
+dd2990[]={1610,0},
+dd2991[]={1604,1570,0},
+dd2992[]={1604,1571,0},
+dd2993[]={1604,1573,0},
+dd2994[]={1604,1575,0},
+dd2995[]={34,0},
+dd2996[]={39,0},
+dd2997[]={47,0},
+dd2998[]={94,0},
+dd2999[]={124,0},
+dd3000[]={126,0},
+dd3001[]={10629,0},
+dd3002[]={10630,0},
+dd3003[]={12539,0},
+dd3004[]={12449,0},
+dd3005[]={12451,0},
+dd3006[]={12453,0},
+dd3007[]={12455,0},
+dd3008[]={12457,0},
+dd3009[]={12515,0},
+dd3010[]={12517,0},
+dd3011[]={12519,0},
+dd3012[]={12483,0},
+dd3013[]={12540,0},
+dd3014[]={12531,0},
+dd3015[]={12441,0},
+dd3016[]={12442,0},
+dd3017[]={12644,0},
+dd3018[]={12593,0},
+dd3019[]={12594,0},
+dd3020[]={12595,0},
+dd3021[]={12596,0},
+dd3022[]={12597,0},
+dd3023[]={12598,0},
+dd3024[]={12599,0},
+dd3025[]={12600,0},
+dd3026[]={12601,0},
+dd3027[]={12602,0},
+dd3028[]={12603,0},
+dd3029[]={12604,0},
+dd3030[]={12605,0},
+dd3031[]={12606,0},
+dd3032[]={12607,0},
+dd3033[]={12608,0},
+dd3034[]={12609,0},
+dd3035[]={12610,0},
+dd3036[]={12611,0},
+dd3037[]={12612,0},
+dd3038[]={12613,0},
+dd3039[]={12614,0},
+dd3040[]={12615,0},
+dd3041[]={12616,0},
+dd3042[]={12617,0},
+dd3043[]={12618,0},
+dd3044[]={12619,0},
+dd3045[]={12620,0},
+dd3046[]={12621,0},
+dd3047[]={12622,0},
+dd3048[]={12623,0},
+dd3049[]={12624,0},
+dd3050[]={12625,0},
+dd3051[]={12626,0},
+dd3052[]={12627,0},
+dd3053[]={12628,0},
+dd3054[]={12629,0},
+dd3055[]={12630,0},
+dd3056[]={12631,0},
+dd3057[]={12632,0},
+dd3058[]={12633,0},
+dd3059[]={12634,0},
+dd3060[]={12635,0},
+dd3061[]={12636,0},
+dd3062[]={12637,0},
+dd3063[]={12638,0},
+dd3064[]={12639,0},
+dd3065[]={12640,0},
+dd3066[]={12641,0},
+dd3067[]={12642,0},
+dd3068[]={12643,0},
+dd3069[]={162,0},
+dd3070[]={163,0},
+dd3071[]={172,0},
+dd3072[]={175,0},
+dd3073[]={166,0},
+dd3074[]={165,0},
+dd3075[]={8361,0},
+dd3076[]={9474,0},
+dd3077[]={8592,0},
+dd3078[]={8593,0},
+dd3079[]={8594,0},
+dd3080[]={8595,0},
+dd3081[]={9632,0},
+dd3082[]={9675,0},
+dd3083[]={119127,119141,0},
+dd3084[]={119128,119141,0},
+dd3085[]={119135,119150,0},
+dd3086[]={119135,119151,0},
+dd3087[]={119135,119152,0},
+dd3088[]={119135,119153,0},
+dd3089[]={119135,119154,0},
+dd3090[]={119225,119141,0},
+dd3091[]={119226,119141,0},
+dd3092[]={119227,119150,0},
+dd3093[]={119228,119150,0},
+dd3094[]={119227,119151,0},
+dd3095[]={119228,119151,0},
+dd3096[]={305,0},
+dd3097[]={567,0},
+dd3098[]={913,0},
+dd3099[]={914,0},
+dd3100[]={916,0},
+dd3101[]={917,0},
+dd3102[]={918,0},
+dd3103[]={919,0},
+dd3104[]={921,0},
+dd3105[]={922,0},
+dd3106[]={923,0},
+dd3107[]={924,0},
+dd3108[]={925,0},
+dd3109[]={926,0},
+dd3110[]={927,0},
+dd3111[]={929,0},
+dd3112[]={1012,0},
+dd3113[]={932,0},
+dd3114[]={934,0},
+dd3115[]={935,0},
+dd3116[]={936,0},
+dd3117[]={8711,0},
+dd3118[]={945,0},
+dd3119[]={950,0},
+dd3120[]={951,0},
+dd3121[]={955,0},
+dd3122[]={957,0},
+dd3123[]={958,0},
+dd3124[]={959,0},
+dd3125[]={963,0},
+dd3126[]={964,0},
+dd3127[]={965,0},
+dd3128[]={968,0},
+dd3129[]={969,0},
+dd3130[]={8706,0},
+dd3131[]={1013,0},
+dd3132[]={977,0},
+dd3133[]={1008,0},
+dd3134[]={981,0},
+dd3135[]={1009,0},
+dd3136[]={982,0},
+dd3137[]={988,0},
+dd3138[]={989,0},
+dd3139[]={20029,0},
+dd3140[]={20024,0},
+dd3141[]={20033,0},
+dd3142[]={131362,0},
+dd3143[]={20320,0},
+dd3144[]={20411,0},
+dd3145[]={20482,0},
+dd3146[]={20602,0},
+dd3147[]={20633,0},
+dd3148[]={20687,0},
+dd3149[]={13470,0},
+dd3150[]={132666,0},
+dd3151[]={20820,0},
+dd3152[]={20836,0},
+dd3153[]={20855,0},
+dd3154[]={132380,0},
+dd3155[]={13497,0},
+dd3156[]={20839,0},
+dd3157[]={20877,0},
+dd3158[]={132427,0},
+dd3159[]={20887,0},
+dd3160[]={20900,0},
+dd3161[]={20172,0},
+dd3162[]={20908,0},
+dd3163[]={168415,0},
+dd3164[]={20995,0},
+dd3165[]={13535,0},
+dd3166[]={21051,0},
+dd3167[]={21062,0},
+dd3168[]={21106,0},
+dd3169[]={21111,0},
+dd3170[]={13589,0},
+dd3171[]={21253,0},
+dd3172[]={21254,0},
+dd3173[]={21321,0},
+dd3174[]={21338,0},
+dd3175[]={21363,0},
+dd3176[]={21373,0},
+dd3177[]={21375,0},
+dd3178[]={133676,0},
+dd3179[]={28784,0},
+dd3180[]={21450,0},
+dd3181[]={21471,0},
+dd3182[]={133987,0},
+dd3183[]={21483,0},
+dd3184[]={21489,0},
+dd3185[]={21510,0},
+dd3186[]={21662,0},
+dd3187[]={21560,0},
+dd3188[]={21576,0},
+dd3189[]={21608,0},
+dd3190[]={21666,0},
+dd3191[]={21750,0},
+dd3192[]={21776,0},
+dd3193[]={21843,0},
+dd3194[]={21859,0},
+dd3195[]={21892,0},
+dd3196[]={21931,0},
+dd3197[]={21939,0},
+dd3198[]={21954,0},
+dd3199[]={22294,0},
+dd3200[]={22295,0},
+dd3201[]={22097,0},
+dd3202[]={22132,0},
+dd3203[]={22766,0},
+dd3204[]={22478,0},
+dd3205[]={22516,0},
+dd3206[]={22541,0},
+dd3207[]={22411,0},
+dd3208[]={22578,0},
+dd3209[]={22577,0},
+dd3210[]={22700,0},
+dd3211[]={136420,0},
+dd3212[]={22770,0},
+dd3213[]={22775,0},
+dd3214[]={22790,0},
+dd3215[]={22810,0},
+dd3216[]={22818,0},
+dd3217[]={22882,0},
+dd3218[]={136872,0},
+dd3219[]={136938,0},
+dd3220[]={23020,0},
+dd3221[]={23067,0},
+dd3222[]={23079,0},
+dd3223[]={23000,0},
+dd3224[]={23142,0},
+dd3225[]={14062,0},
+dd3226[]={14076,0},
+dd3227[]={23304,0},
+dd3228[]={23358,0},
+dd3229[]={137672,0},
+dd3230[]={23491,0},
+dd3231[]={23512,0},
+dd3232[]={23539,0},
+dd3233[]={138008,0},
+dd3234[]={23551,0},
+dd3235[]={23558,0},
+dd3236[]={24403,0},
+dd3237[]={14209,0},
+dd3238[]={23648,0},
+dd3239[]={23744,0},
+dd3240[]={23693,0},
+dd3241[]={138724,0},
+dd3242[]={23875,0},
+dd3243[]={138726,0},
+dd3244[]={23918,0},
+dd3245[]={23915,0},
+dd3246[]={23932,0},
+dd3247[]={24033,0},
+dd3248[]={24034,0},
+dd3249[]={14383,0},
+dd3250[]={24061,0},
+dd3251[]={24104,0},
+dd3252[]={24125,0},
+dd3253[]={24169,0},
+dd3254[]={14434,0},
+dd3255[]={139651,0},
+dd3256[]={14460,0},
+dd3257[]={24240,0},
+dd3258[]={24243,0},
+dd3259[]={24246,0},
+dd3260[]={172946,0},
+dd3261[]={140081,0},
+dd3262[]={33281,0},
+dd3263[]={24354,0},
+dd3264[]={14535,0},
+dd3265[]={144056,0},
+dd3266[]={156122,0},
+dd3267[]={24418,0},
+dd3268[]={24427,0},
+dd3269[]={14563,0},
+dd3270[]={24474,0},
+dd3271[]={24525,0},
+dd3272[]={24535,0},
+dd3273[]={24569,0},
+dd3274[]={24705,0},
+dd3275[]={14650,0},
+dd3276[]={14620,0},
+dd3277[]={141012,0},
+dd3278[]={24775,0},
+dd3279[]={24904,0},
+dd3280[]={24908,0},
+dd3281[]={24954,0},
+dd3282[]={25010,0},
+dd3283[]={24996,0},
+dd3284[]={25007,0},
+dd3285[]={25054,0},
+dd3286[]={25104,0},
+dd3287[]={25115,0},
+dd3288[]={25181,0},
+dd3289[]={25265,0},
+dd3290[]={25300,0},
+dd3291[]={25424,0},
+dd3292[]={142092,0},
+dd3293[]={25405,0},
+dd3294[]={25340,0},
+dd3295[]={25448,0},
+dd3296[]={25475,0},
+dd3297[]={25572,0},
+dd3298[]={142321,0},
+dd3299[]={25634,0},
+dd3300[]={25541,0},
+dd3301[]={25513,0},
+dd3302[]={14894,0},
+dd3303[]={25705,0},
+dd3304[]={25726,0},
+dd3305[]={25757,0},
+dd3306[]={25719,0},
+dd3307[]={14956,0},
+dd3308[]={25964,0},
+dd3309[]={143370,0},
+dd3310[]={26083,0},
+dd3311[]={26360,0},
+dd3312[]={26185,0},
+dd3313[]={15129,0},
+dd3314[]={15112,0},
+dd3315[]={15076,0},
+dd3316[]={20882,0},
+dd3317[]={20885,0},
+dd3318[]={26368,0},
+dd3319[]={26268,0},
+dd3320[]={32941,0},
+dd3321[]={17369,0},
+dd3322[]={26401,0},
+dd3323[]={26462,0},
+dd3324[]={26451,0},
+dd3325[]={144323,0},
+dd3326[]={15177,0},
+dd3327[]={26618,0},
+dd3328[]={26501,0},
+dd3329[]={26706,0},
+dd3330[]={144493,0},
+dd3331[]={26766,0},
+dd3332[]={26655,0},
+dd3333[]={26900,0},
+dd3334[]={26946,0},
+dd3335[]={27043,0},
+dd3336[]={27114,0},
+dd3337[]={27304,0},
+dd3338[]={145059,0},
+dd3339[]={27355,0},
+dd3340[]={15384,0},
+dd3341[]={27425,0},
+dd3342[]={145575,0},
+dd3343[]={27476,0},
+dd3344[]={15438,0},
+dd3345[]={27506,0},
+dd3346[]={27551,0},
+dd3347[]={27579,0},
+dd3348[]={146061,0},
+dd3349[]={138507,0},
+dd3350[]={146170,0},
+dd3351[]={27726,0},
+dd3352[]={146620,0},
+dd3353[]={27839,0},
+dd3354[]={27853,0},
+dd3355[]={27751,0},
+dd3356[]={27926,0},
+dd3357[]={27966,0},
+dd3358[]={28009,0},
+dd3359[]={28024,0},
+dd3360[]={28037,0},
+dd3361[]={146718,0},
+dd3362[]={27956,0},
+dd3363[]={28207,0},
+dd3364[]={28270,0},
+dd3365[]={15667,0},
+dd3366[]={28359,0},
+dd3367[]={147153,0},
+dd3368[]={28153,0},
+dd3369[]={28526,0},
+dd3370[]={147294,0},
+dd3371[]={147342,0},
+dd3372[]={28614,0},
+dd3373[]={28729,0},
+dd3374[]={28699,0},
+dd3375[]={15766,0},
+dd3376[]={28746,0},
+dd3377[]={28797,0},
+dd3378[]={28791,0},
+dd3379[]={28845,0},
+dd3380[]={132389,0},
+dd3381[]={28997,0},
+dd3382[]={148067,0},
+dd3383[]={29084,0},
+dd3384[]={148395,0},
+dd3385[]={29224,0},
+dd3386[]={29264,0},
+dd3387[]={149000,0},
+dd3388[]={29312,0},
+dd3389[]={29333,0},
+dd3390[]={149301,0},
+dd3391[]={149524,0},
+dd3392[]={29562,0},
+dd3393[]={29579,0},
+dd3394[]={16044,0},
+dd3395[]={29605,0},
+dd3396[]={16056,0},
+dd3397[]={29767,0},
+dd3398[]={29788,0},
+dd3399[]={29829,0},
+dd3400[]={29898,0},
+dd3401[]={16155,0},
+dd3402[]={29988,0},
+dd3403[]={150582,0},
+dd3404[]={30014,0},
+dd3405[]={150674,0},
+dd3406[]={139679,0},
+dd3407[]={30224,0},
+dd3408[]={151457,0},
+dd3409[]={151480,0},
+dd3410[]={151620,0},
+dd3411[]={16380,0},
+dd3412[]={16392,0},
+dd3413[]={151795,0},
+dd3414[]={151794,0},
+dd3415[]={151833,0},
+dd3416[]={151859,0},
+dd3417[]={30494,0},
+dd3418[]={30495,0},
+dd3419[]={30603,0},
+dd3420[]={16454,0},
+dd3421[]={16534,0},
+dd3422[]={152605,0},
+dd3423[]={30798,0},
+dd3424[]={16611,0},
+dd3425[]={153126,0},
+dd3426[]={153242,0},
+dd3427[]={153285,0},
+dd3428[]={31211,0},
+dd3429[]={16687,0},
+dd3430[]={31306,0},
+dd3431[]={31311,0},
+dd3432[]={153980,0},
+dd3433[]={154279,0},
+dd3434[]={31470,0},
+dd3435[]={16898,0},
+dd3436[]={154539,0},
+dd3437[]={31686,0},
+dd3438[]={31689,0},
+dd3439[]={16935,0},
+dd3440[]={154752,0},
+dd3441[]={31954,0},
+dd3442[]={17056,0},
+dd3443[]={31976,0},
+dd3444[]={31971,0},
+dd3445[]={32000,0},
+dd3446[]={155526,0},
+dd3447[]={32099,0},
+dd3448[]={17153,0},
+dd3449[]={32199,0},
+dd3450[]={32258,0},
+dd3451[]={32325,0},
+dd3452[]={17204,0},
+dd3453[]={156200,0},
+dd3454[]={156231,0},
+dd3455[]={17241,0},
+dd3456[]={156377,0},
+dd3457[]={32634,0},
+dd3458[]={156478,0},
+dd3459[]={32661,0},
+dd3460[]={32762,0},
+dd3461[]={156890,0},
+dd3462[]={156963,0},
+dd3463[]={32864,0},
+dd3464[]={157096,0},
+dd3465[]={32880,0},
+dd3466[]={144223,0},
+dd3467[]={17365,0},
+dd3468[]={32946,0},
+dd3469[]={33027,0},
+dd3470[]={17419,0},
+dd3471[]={33086,0},
+dd3472[]={23221,0},
+dd3473[]={157607,0},
+dd3474[]={157621,0},
+dd3475[]={144275,0},
+dd3476[]={144284,0},
+dd3477[]={33284,0},
+dd3478[]={36766,0},
+dd3479[]={17515,0},
+dd3480[]={33425,0},
+dd3481[]={33419,0},
+dd3482[]={33437,0},
+dd3483[]={21171,0},
+dd3484[]={33457,0},
+dd3485[]={33459,0},
+dd3486[]={33469,0},
+dd3487[]={33510,0},
+dd3488[]={158524,0},
+dd3489[]={33565,0},
+dd3490[]={33635,0},
+dd3491[]={33709,0},
+dd3492[]={33571,0},
+dd3493[]={33725,0},
+dd3494[]={33767,0},
+dd3495[]={33619,0},
+dd3496[]={33738,0},
+dd3497[]={33740,0},
+dd3498[]={33756,0},
+dd3499[]={158774,0},
+dd3500[]={159083,0},
+dd3501[]={158933,0},
+dd3502[]={17707,0},
+dd3503[]={34033,0},
+dd3504[]={34035,0},
+dd3505[]={34070,0},
+dd3506[]={160714,0},
+dd3507[]={34148,0},
+dd3508[]={159532,0},
+dd3509[]={17757,0},
+dd3510[]={17761,0},
+dd3511[]={159665,0},
+dd3512[]={159954,0},
+dd3513[]={17771,0},
+dd3514[]={34384,0},
+dd3515[]={34407,0},
+dd3516[]={34409,0},
+dd3517[]={34473,0},
+dd3518[]={34440,0},
+dd3519[]={34574,0},
+dd3520[]={34530,0},
+dd3521[]={34600,0},
+dd3522[]={34667,0},
+dd3523[]={34694,0},
+dd3524[]={17879,0},
+dd3525[]={34785,0},
+dd3526[]={34817,0},
+dd3527[]={17913,0},
+dd3528[]={34912,0},
+dd3529[]={161383,0},
+dd3530[]={35031,0},
+dd3531[]={35038,0},
+dd3532[]={17973,0},
+dd3533[]={35066,0},
+dd3534[]={13499,0},
+dd3535[]={161966,0},
+dd3536[]={162150,0},
+dd3537[]={18110,0},
+dd3538[]={18119,0},
+dd3539[]={35488,0},
+dd3540[]={162984,0},
+dd3541[]={36011,0},
+dd3542[]={36033,0},
+dd3543[]={36123,0},
+dd3544[]={36215,0},
+dd3545[]={163631,0},
+dd3546[]={133124,0},
+dd3547[]={36299,0},
+dd3548[]={36284,0},
+dd3549[]={36336,0},
+dd3550[]={133342,0},
+dd3551[]={36564,0},
+dd3552[]={165330,0},
+dd3553[]={165357,0},
+dd3554[]={37012,0},
+dd3555[]={37105,0},
+dd3556[]={37137,0},
+dd3557[]={165678,0},
+dd3558[]={37147,0},
+dd3559[]={37432,0},
+dd3560[]={37591,0},
+dd3561[]={37592,0},
+dd3562[]={37500,0},
+dd3563[]={37881,0},
+dd3564[]={37909,0},
+dd3565[]={166906,0},
+dd3566[]={38283,0},
+dd3567[]={18837,0},
+dd3568[]={38327,0},
+dd3569[]={167287,0},
+dd3570[]={18918,0},
+dd3571[]={38595,0},
+dd3572[]={23986,0},
+dd3573[]={38691,0},
+dd3574[]={168261,0},
+dd3575[]={168474,0},
+dd3576[]={19054,0},
+dd3577[]={19062,0},
+dd3578[]={38880,0},
+dd3579[]={168970,0},
+dd3580[]={19122,0},
+dd3581[]={169110,0},
+dd3582[]={38953,0},
+dd3583[]={169398,0},
+dd3584[]={39138,0},
+dd3585[]={19251,0},
+dd3586[]={39209,0},
+dd3587[]={39335,0},
+dd3588[]={39362,0},
+dd3589[]={39422,0},
+dd3590[]={19406,0},
+dd3591[]={170800,0},
+dd3592[]={40000,0},
+dd3593[]={40189,0},
+dd3594[]={19662,0},
+dd3595[]={19693,0},
+dd3596[]={40295,0},
+dd3597[]={172238,0},
+dd3598[]={19704,0},
+dd3599[]={172293,0},
+dd3600[]={172558,0},
+dd3601[]={172689,0},
+dd3602[]={19798,0},
+dd3603[]={40702,0},
+dd3604[]={40709,0},
+dd3605[]={40719,0},
+dd3606[]={40726,0},
+dd3607[]={173568,0},
+dd3608[]={8814,0},
+dd3609[]={8800,0},
+dd3610[]={8815,0},
+dd3611[]={192,193,194,195,196,197,256,258,260,461,512,514,550,7680,7840,7842,0},
+dd3612[]={7682,7684,7686,0},
+dd3613[]={199,262,264,266,268,0},
+dd3614[]={270,7690,7692,7694,7696,7698,0},
+dd3615[]={200,201,202,203,274,276,278,280,282,516,518,552,7704,7706,7864,7866,7868,0},
+dd3616[]={7710,0},
+dd3617[]={284,286,288,290,486,500,7712,0},
+dd3618[]={292,542,7714,7716,7718,7720,7722,0},
+dd3619[]={204,205,206,207,296,298,300,302,304,463,520,522,7724,7880,7882,0},
+dd3620[]={308,0},
+dd3621[]={310,488,7728,7730,7732,0},
+dd3622[]={313,315,317,7734,7738,7740,0},
+dd3623[]={7742,7744,7746,0},
+dd3624[]={209,323,325,327,504,7748,7750,7752,7754,0},
+dd3625[]={210,211,212,213,214,332,334,336,416,465,490,524,526,558,7884,7886,0},
+dd3626[]={7764,7766,0},
+dd3627[]={340,342,344,528,530,7768,7770,7774,0},
+dd3628[]={346,348,350,352,536,7776,7778,0},
+dd3629[]={354,356,538,7786,7788,7790,7792,0},
+dd3630[]={217,218,219,220,360,362,364,366,368,370,431,467,532,534,7794,7796,7798,7908,7910,0},
+dd3631[]={7804,7806,0},
+dd3632[]={372,7808,7810,7812,7814,7816,0},
+dd3633[]={7818,7820,0},
+dd3634[]={221,374,376,562,7822,7922,7924,7926,7928,0},
+dd3635[]={377,379,381,7824,7826,7828,0},
+dd3636[]={224,225,226,227,228,229,257,259,261,462,513,515,551,7681,7841,7843,0},
+dd3637[]={7683,7685,7687,0},
+dd3638[]={231,263,265,267,269,0},
+dd3639[]={271,7691,7693,7695,7697,7699,0},
+dd3640[]={232,233,234,235,275,277,279,281,283,517,519,553,7705,7707,7865,7867,7869,0},
+dd3641[]={7711,0},
+dd3642[]={285,287,289,291,487,501,7713,0},
+dd3643[]={293,543,7715,7717,7719,7721,7723,7830,0},
+dd3644[]={236,237,238,239,297,299,301,303,464,521,523,7725,7881,7883,0},
+dd3645[]={309,496,0},
+dd3646[]={311,489,7729,7731,7733,0},
+dd3647[]={314,316,318,7735,7739,7741,0},
+dd3648[]={7743,7745,7747,0},
+dd3649[]={241,324,326,328,505,7749,7751,7753,7755,0},
+dd3650[]={242,243,244,245,246,333,335,337,417,466,491,525,527,559,7885,7887,0},
+dd3651[]={7765,7767,0},
+dd3652[]={341,343,345,529,531,7769,7771,7775,0},
+dd3653[]={347,349,351,353,537,7777,7779,0},
+dd3654[]={355,357,539,7787,7789,7791,7793,7831,0},
+dd3655[]={249,250,251,252,361,363,365,367,369,371,432,468,533,535,7795,7797,7799,7909,7911,0},
+dd3656[]={7805,7807,0},
+dd3657[]={373,7809,7811,7813,7815,7817,7832,0},
+dd3658[]={7819,7821,0},
+dd3659[]={253,255,375,563,7823,7833,7923,7925,7927,7929,0},
+dd3660[]={378,380,382,7825,7827,7829,0},
+dd3661[]={901,8129,8173,0},
+dd3662[]={7844,7846,7848,7850,0},
+dd3663[]={478,0},
+dd3664[]={506,0},
+dd3665[]={482,508,0},
+dd3666[]={7688,0},
+dd3667[]={7870,7872,7874,7876,0},
+dd3668[]={7726,0},
+dd3669[]={7888,7890,7892,7894,0},
+dd3670[]={556,7756,7758,0},
+dd3671[]={554,0},
+dd3672[]={510,0},
+dd3673[]={469,471,473,475,0},
+dd3674[]={7845,7847,7849,7851,0},
+dd3675[]={479,0},
+dd3676[]={507,0},
+dd3677[]={483,509,0},
+dd3678[]={7689,0},
+dd3679[]={7871,7873,7875,7877,0},
+dd3680[]={7727,0},
+dd3681[]={7889,7891,7893,7895,0},
+dd3682[]={557,7757,7759,0},
+dd3683[]={555,0},
+dd3684[]={511,0},
+dd3685[]={470,472,474,476,0},
+dd3686[]={7854,7856,7858,7860,0},
+dd3687[]={7855,7857,7859,7861,0},
+dd3688[]={7700,7702,0},
+dd3689[]={7701,7703,0},
+dd3690[]={7760,7762,0},
+dd3691[]={7761,7763,0},
+dd3692[]={7780,0},
+dd3693[]={7781,0},
+dd3694[]={7782,0},
+dd3695[]={7783,0},
+dd3696[]={7800,0},
+dd3697[]={7801,0},
+dd3698[]={7802,0},
+dd3699[]={7803,0},
+dd3700[]={7835,0},
+dd3701[]={7898,7900,7902,7904,7906,0},
+dd3702[]={7899,7901,7903,7905,7907,0},
+dd3703[]={7912,7914,7916,7918,7920,0},
+dd3704[]={7913,7915,7917,7919,7921,0},
+dd3705[]={494,0},
+dd3706[]={492,0},
+dd3707[]={493,0},
+dd3708[]={480,0},
+dd3709[]={481,0},
+dd3710[]={7708,0},
+dd3711[]={7709,0},
+dd3712[]={560,0},
+dd3713[]={561,0},
+dd3714[]={495,0},
+dd3715[]={902,7944,7945,8120,8121,8122,8124,0},
+dd3716[]={904,7960,7961,8136,0},
+dd3717[]={905,7976,7977,8138,8140,0},
+dd3718[]={906,938,7992,7993,8152,8153,8154,0},
+dd3719[]={908,8008,8009,8184,0},
+dd3720[]={8172,0},
+dd3721[]={910,939,8025,8168,8169,8170,0},
+dd3722[]={911,8040,8041,8186,8188,0},
+dd3723[]={8116,0},
+dd3724[]={8132,0},
+dd3725[]={940,7936,7937,8048,8112,8113,8115,8118,0},
+dd3726[]={941,7952,7953,8050,0},
+dd3727[]={942,7968,7969,8052,8131,8134,0},
+dd3728[]={943,970,7984,7985,8054,8144,8145,8150,0},
+dd3729[]={972,8000,8001,8056,0},
+dd3730[]={8164,8165,0},
+dd3731[]={971,973,8016,8017,8058,8160,8161,8166,0},
+dd3732[]={974,8032,8033,8060,8179,8182,0},
+dd3733[]={912,8146,8151,0},
+dd3734[]={944,8162,8167,0},
+dd3735[]={8180,0},
+dd3736[]={979,980,0},
+dd3737[]={1031,0},
+dd3738[]={1232,1234,0},
+dd3739[]={1027,0},
+dd3740[]={1024,1025,1238,0},
+dd3741[]={1217,1244,0},
+dd3742[]={1246,0},
+dd3743[]={1037,1049,1250,1252,0},
+dd3744[]={1036,0},
+dd3745[]={1254,0},
+dd3746[]={1038,1262,1264,1266,0},
+dd3747[]={1268,0},
+dd3748[]={1272,0},
+dd3749[]={1260,0},
+dd3750[]={1233,1235,0},
+dd3751[]={1107,0},
+dd3752[]={1104,1105,1239,0},
+dd3753[]={1218,1245,0},
+dd3754[]={1247,0},
+dd3755[]={1081,1117,1251,1253,0},
+dd3756[]={1116,0},
+dd3757[]={1255,0},
+dd3758[]={1118,1263,1265,1267,0},
+dd3759[]={1269,0},
+dd3760[]={1273,0},
+dd3761[]={1261,0},
+dd3762[]={1111,0},
+dd3763[]={1142,0},
+dd3764[]={1143,0},
+dd3765[]={1242,0},
+dd3766[]={1243,0},
+dd3767[]={1258,0},
+dd3768[]={1259,0},
+dd3769[]={1570,1571,1573,0},
+dd3770[]={1730,0},
+dd3771[]={2345,0},
+dd3772[]={2353,0},
+dd3773[]={2356,0},
+dd3774[]={2507,2508,0},
+dd3775[]={2888,2891,2892,0},
+dd3776[]={2964,0},
+dd3777[]={3018,3020,0},
+dd3778[]={3019,0},
+dd3779[]={3144,0},
+dd3780[]={3264,0},
+dd3781[]={3271,3272,3274,0},
+dd3782[]={3275,0},
+dd3783[]={3402,3404,0},
+dd3784[]={3403,0},
+dd3785[]={3546,3548,3550,0},
+dd3786[]={3549,0},
+dd3787[]={4134,0},
+dd3788[]={6918,0},
+dd3789[]={6920,0},
+dd3790[]={6922,0},
+dd3791[]={6924,0},
+dd3792[]={6926,0},
+dd3793[]={6930,0},
+dd3794[]={6971,0},
+dd3795[]={6973,0},
+dd3796[]={6976,0},
+dd3797[]={6977,0},
+dd3798[]={6979,0},
+dd3799[]={7736,0},
+dd3800[]={7737,0},
+dd3801[]={7772,0},
+dd3802[]={7773,0},
+dd3803[]={7784,0},
+dd3804[]={7785,0},
+dd3805[]={7852,7862,0},
+dd3806[]={7853,7863,0},
+dd3807[]={7878,0},
+dd3808[]={7879,0},
+dd3809[]={7896,0},
+dd3810[]={7897,0},
+dd3811[]={7938,7940,7942,8064,0},
+dd3812[]={7939,7941,7943,8065,0},
+dd3813[]={8066,0},
+dd3814[]={8067,0},
+dd3815[]={8068,0},
+dd3816[]={8069,0},
+dd3817[]={8070,0},
+dd3818[]={8071,0},
+dd3819[]={7946,7948,7950,8072,0},
+dd3820[]={7947,7949,7951,8073,0},
+dd3821[]={8074,0},
+dd3822[]={8075,0},
+dd3823[]={8076,0},
+dd3824[]={8077,0},
+dd3825[]={8078,0},
+dd3826[]={8079,0},
+dd3827[]={7954,7956,0},
+dd3828[]={7955,7957,0},
+dd3829[]={7962,7964,0},
+dd3830[]={7963,7965,0},
+dd3831[]={7970,7972,7974,8080,0},
+dd3832[]={7971,7973,7975,8081,0},
+dd3833[]={8082,0},
+dd3834[]={8083,0},
+dd3835[]={8084,0},
+dd3836[]={8085,0},
+dd3837[]={8086,0},
+dd3838[]={8087,0},
+dd3839[]={7978,7980,7982,8088,0},
+dd3840[]={7979,7981,7983,8089,0},
+dd3841[]={8090,0},
+dd3842[]={8091,0},
+dd3843[]={8092,0},
+dd3844[]={8093,0},
+dd3845[]={8094,0},
+dd3846[]={8095,0},
+dd3847[]={7986,7988,7990,0},
+dd3848[]={7987,7989,7991,0},
+dd3849[]={7994,7996,7998,0},
+dd3850[]={7995,7997,7999,0},
+dd3851[]={8002,8004,0},
+dd3852[]={8003,8005,0},
+dd3853[]={8010,8012,0},
+dd3854[]={8011,8013,0},
+dd3855[]={8018,8020,8022,0},
+dd3856[]={8019,8021,8023,0},
+dd3857[]={8027,8029,8031,0},
+dd3858[]={8034,8036,8038,8096,0},
+dd3859[]={8035,8037,8039,8097,0},
+dd3860[]={8098,0},
+dd3861[]={8099,0},
+dd3862[]={8100,0},
+dd3863[]={8101,0},
+dd3864[]={8102,0},
+dd3865[]={8103,0},
+dd3866[]={8042,8044,8046,8104,0},
+dd3867[]={8043,8045,8047,8105,0},
+dd3868[]={8106,0},
+dd3869[]={8107,0},
+dd3870[]={8108,0},
+dd3871[]={8109,0},
+dd3872[]={8110,0},
+dd3873[]={8111,0},
+dd3874[]={8114,0},
+dd3875[]={8130,0},
+dd3876[]={8178,0},
+dd3877[]={8119,0},
+dd3878[]={8141,8142,8143,0},
+dd3879[]={8135,0},
+dd3880[]={8183,0},
+dd3881[]={8157,8158,8159,0},
+dd3882[]={8602,0},
+dd3883[]={8603,0},
+dd3884[]={8622,0},
+dd3885[]={8653,0},
+dd3886[]={8655,0},
+dd3887[]={8654,0},
+dd3888[]={8708,0},
+dd3889[]={8713,0},
+dd3890[]={8716,0},
+dd3891[]={8740,0},
+dd3892[]={8742,0},
+dd3893[]={8769,0},
+dd3894[]={8772,0},
+dd3895[]={8775,0},
+dd3896[]={8777,0},
+dd3897[]={8813,0},
+dd3898[]={8802,0},
+dd3899[]={8816,0},
+dd3900[]={8817,0},
+dd3901[]={8820,0},
+dd3902[]={8821,0},
+dd3903[]={8824,0},
+dd3904[]={8825,0},
+dd3905[]={8832,0},
+dd3906[]={8833,0},
+dd3907[]={8928,0},
+dd3908[]={8929,0},
+dd3909[]={8836,0},
+dd3910[]={8837,0},
+dd3911[]={8840,0},
+dd3912[]={8841,0},
+dd3913[]={8930,0},
+dd3914[]={8931,0},
+dd3915[]={8876,0},
+dd3916[]={8877,0},
+dd3917[]={8878,0},
+dd3918[]={8879,0},
+dd3919[]={8938,0},
+dd3920[]={8939,0},
+dd3921[]={8940,0},
+dd3922[]={8941,0},
+dd3923[]={12436,0},
+dd3924[]={12364,0},
+dd3925[]={12366,0},
+dd3926[]={12368,0},
+dd3927[]={12370,0},
+dd3928[]={12372,0},
+dd3929[]={12374,0},
+dd3930[]={12376,0},
+dd3931[]={12378,0},
+dd3932[]={12380,0},
+dd3933[]={12382,0},
+dd3934[]={12384,0},
+dd3935[]={12386,0},
+dd3936[]={12389,0},
+dd3937[]={12391,0},
+dd3938[]={12393,0},
+dd3939[]={12400,12401,0},
+dd3940[]={12403,12404,0},
+dd3941[]={12406,12407,0},
+dd3942[]={12409,12410,0},
+dd3943[]={12412,12413,0},
+dd3944[]={12446,0},
+dd3945[]={12532,0},
+dd3946[]={12460,0},
+dd3947[]={12462,0},
+dd3948[]={12464,0},
+dd3949[]={12466,0},
+dd3950[]={12468,0},
+dd3951[]={12470,0},
+dd3952[]={12472,0},
+dd3953[]={12474,0},
+dd3954[]={12476,0},
+dd3955[]={12478,0},
+dd3956[]={12480,0},
+dd3957[]={12482,0},
+dd3958[]={12485,0},
+dd3959[]={12487,0},
+dd3960[]={12489,0},
+dd3961[]={12496,12497,0},
+dd3962[]={12499,12500,0},
+dd3963[]={12502,12503,0},
+dd3964[]={12505,12506,0},
+dd3965[]={12508,12509,0},
+dd3966[]={12535,0},
+dd3967[]={12536,0},
+dd3968[]={12537,0},
+dd3969[]={12538,0},
+dd3970[]={12542,0},
+dd3971[]={224,0},
+dd3972[]={225,0},
+dd3973[]={226,0},
+dd3974[]={227,0},
+dd3975[]={228,0},
+dd3976[]={229,0},
+dd3977[]={230,0},
+dd3978[]={231,0},
+dd3979[]={232,0},
+dd3980[]={233,0},
+dd3981[]={234,0},
+dd3982[]={235,0},
+dd3983[]={236,0},
+dd3984[]={237,0},
+dd3985[]={238,0},
+dd3986[]={239,0},
+dd3987[]={241,0},
+dd3988[]={242,0},
+dd3989[]={243,0},
+dd3990[]={244,0},
+dd3991[]={245,0},
+dd3992[]={246,0},
+dd3993[]={248,0},
+dd3994[]={249,0},
+dd3995[]={250,0},
+dd3996[]={251,0},
+dd3997[]={252,0},
+dd3998[]={253,0},
+dd3999[]={254,0},
+dd4000[]={115,115,0},
+dd4001[]={257,0},
+dd4002[]={259,0},
+dd4003[]={261,0},
+dd4004[]={263,0},
+dd4005[]={265,0},
+dd4006[]={267,0},
+dd4007[]={269,0},
+dd4008[]={271,0},
+dd4009[]={273,0},
+dd4010[]={275,0},
+dd4011[]={277,0},
+dd4012[]={279,0},
+dd4013[]={281,0},
+dd4014[]={283,0},
+dd4015[]={285,0},
+dd4016[]={287,0},
+dd4017[]={289,0},
+dd4018[]={291,0},
+dd4019[]={293,0},
+dd4020[]={297,0},
+dd4021[]={299,0},
+dd4022[]={301,0},
+dd4023[]={303,0},
+dd4024[]={105,775,0},
+dd4025[]={307,0},
+dd4026[]={309,0},
+dd4027[]={311,0},
+dd4028[]={314,0},
+dd4029[]={316,0},
+dd4030[]={318,0},
+dd4031[]={320,0},
+dd4032[]={322,0},
+dd4033[]={324,0},
+dd4034[]={326,0},
+dd4035[]={328,0},
+dd4036[]={333,0},
+dd4037[]={335,0},
+dd4038[]={337,0},
+dd4039[]={339,0},
+dd4040[]={341,0},
+dd4041[]={343,0},
+dd4042[]={345,0},
+dd4043[]={347,0},
+dd4044[]={349,0},
+dd4045[]={351,0},
+dd4046[]={353,0},
+dd4047[]={355,0},
+dd4048[]={357,0},
+dd4049[]={359,0},
+dd4050[]={361,0},
+dd4051[]={363,0},
+dd4052[]={365,0},
+dd4053[]={367,0},
+dd4054[]={369,0},
+dd4055[]={371,0},
+dd4056[]={373,0},
+dd4057[]={375,0},
+dd4058[]={255,0},
+dd4059[]={378,0},
+dd4060[]={380,0},
+dd4061[]={382,0},
+dd4062[]={595,0},
+dd4063[]={387,0},
+dd4064[]={389,0},
+dd4065[]={392,0},
+dd4066[]={598,0},
+dd4067[]={599,0},
+dd4068[]={396,0},
+dd4069[]={477,0},
+dd4070[]={402,0},
+dd4071[]={608,0},
+dd4072[]={409,0},
+dd4073[]={417,0},
+dd4074[]={419,0},
+dd4075[]={421,0},
+dd4076[]={640,0},
+dd4077[]={424,0},
+dd4078[]={429,0},
+dd4079[]={648,0},
+dd4080[]={432,0},
+dd4081[]={436,0},
+dd4082[]={438,0},
+dd4083[]={441,0},
+dd4084[]={445,0},
+dd4085[]={454,0},
+dd4086[]={457,0},
+dd4087[]={460,0},
+dd4088[]={462,0},
+dd4089[]={464,0},
+dd4090[]={466,0},
+dd4091[]={468,0},
+dd4092[]={470,0},
+dd4093[]={472,0},
+dd4094[]={474,0},
+dd4095[]={476,0},
+dd4096[]={483,0},
+dd4097[]={485,0},
+dd4098[]={487,0},
+dd4099[]={489,0},
+dd4100[]={491,0},
+dd4101[]={499,0},
+dd4102[]={501,0},
+dd4103[]={405,0},
+dd4104[]={447,0},
+dd4105[]={505,0},
+dd4106[]={509,0},
+dd4107[]={513,0},
+dd4108[]={515,0},
+dd4109[]={517,0},
+dd4110[]={519,0},
+dd4111[]={521,0},
+dd4112[]={523,0},
+dd4113[]={525,0},
+dd4114[]={527,0},
+dd4115[]={529,0},
+dd4116[]={531,0},
+dd4117[]={533,0},
+dd4118[]={535,0},
+dd4119[]={537,0},
+dd4120[]={539,0},
+dd4121[]={541,0},
+dd4122[]={543,0},
+dd4123[]={414,0},
+dd4124[]={547,0},
+dd4125[]={549,0},
+dd4126[]={551,0},
+dd4127[]={553,0},
+dd4128[]={557,0},
+dd4129[]={559,0},
+dd4130[]={563,0},
+dd4131[]={11365,0},
+dd4132[]={572,0},
+dd4133[]={410,0},
+dd4134[]={11366,0},
+dd4135[]={578,0},
+dd4136[]={384,0},
+dd4137[]={583,0},
+dd4138[]={585,0},
+dd4139[]={587,0},
+dd4140[]={589,0},
+dd4141[]={591,0},
+dd4142[]={881,0},
+dd4143[]={883,0},
+dd4144[]={887,0},
+dd4145[]={953,776,769,0},
+dd4146[]={970,0},
+dd4147[]={971,0},
+dd4148[]={965,776,769,0},
+dd4149[]={983,0},
+dd4150[]={985,0},
+dd4151[]={987,0},
+dd4152[]={991,0},
+dd4153[]={993,0},
+dd4154[]={995,0},
+dd4155[]={997,0},
+dd4156[]={999,0},
+dd4157[]={1001,0},
+dd4158[]={1003,0},
+dd4159[]={1005,0},
+dd4160[]={1007,0},
+dd4161[]={1016,0},
+dd4162[]={1010,0},
+dd4163[]={1019,0},
+dd4164[]={891,0},
+dd4165[]={892,0},
+dd4166[]={893,0},
+dd4167[]={1104,0},
+dd4168[]={1105,0},
+dd4169[]={1106,0},
+dd4170[]={1108,0},
+dd4171[]={1109,0},
+dd4172[]={1110,0},
+dd4173[]={1112,0},
+dd4174[]={1113,0},
+dd4175[]={1114,0},
+dd4176[]={1115,0},
+dd4177[]={1117,0},
+dd4178[]={1118,0},
+dd4179[]={1119,0},
+dd4180[]={1072,0},
+dd4181[]={1073,0},
+dd4182[]={1074,0},
+dd4183[]={1075,0},
+dd4184[]={1076,0},
+dd4185[]={1077,0},
+dd4186[]={1078,0},
+dd4187[]={1079,0},
+dd4188[]={1080,0},
+dd4189[]={1081,0},
+dd4190[]={1082,0},
+dd4191[]={1083,0},
+dd4192[]={1084,0},
+dd4193[]={1086,0},
+dd4194[]={1087,0},
+dd4195[]={1088,0},
+dd4196[]={1089,0},
+dd4197[]={1090,0},
+dd4198[]={1091,0},
+dd4199[]={1092,0},
+dd4200[]={1093,0},
+dd4201[]={1094,0},
+dd4202[]={1095,0},
+dd4203[]={1096,0},
+dd4204[]={1097,0},
+dd4205[]={1098,0},
+dd4206[]={1099,0},
+dd4207[]={1100,0},
+dd4208[]={1101,0},
+dd4209[]={1102,0},
+dd4210[]={1103,0},
+dd4211[]={1121,0},
+dd4212[]={1123,0},
+dd4213[]={1125,0},
+dd4214[]={1127,0},
+dd4215[]={1129,0},
+dd4216[]={1131,0},
+dd4217[]={1133,0},
+dd4218[]={1135,0},
+dd4219[]={1137,0},
+dd4220[]={1139,0},
+dd4221[]={1141,0},
+dd4222[]={1145,0},
+dd4223[]={1147,0},
+dd4224[]={1149,0},
+dd4225[]={1151,0},
+dd4226[]={1153,0},
+dd4227[]={1163,0},
+dd4228[]={1165,0},
+dd4229[]={1167,0},
+dd4230[]={1169,0},
+dd4231[]={1171,0},
+dd4232[]={1173,0},
+dd4233[]={1175,0},
+dd4234[]={1177,0},
+dd4235[]={1179,0},
+dd4236[]={1181,0},
+dd4237[]={1183,0},
+dd4238[]={1185,0},
+dd4239[]={1187,0},
+dd4240[]={1189,0},
+dd4241[]={1191,0},
+dd4242[]={1193,0},
+dd4243[]={1195,0},
+dd4244[]={1197,0},
+dd4245[]={1199,0},
+dd4246[]={1201,0},
+dd4247[]={1203,0},
+dd4248[]={1205,0},
+dd4249[]={1207,0},
+dd4250[]={1209,0},
+dd4251[]={1211,0},
+dd4252[]={1213,0},
+dd4253[]={1215,0},
+dd4254[]={1231,0},
+dd4255[]={1218,0},
+dd4256[]={1220,0},
+dd4257[]={1222,0},
+dd4258[]={1224,0},
+dd4259[]={1226,0},
+dd4260[]={1228,0},
+dd4261[]={1230,0},
+dd4262[]={1233,0},
+dd4263[]={1235,0},
+dd4264[]={1237,0},
+dd4265[]={1239,0},
+dd4266[]={1241,0},
+dd4267[]={1245,0},
+dd4268[]={1249,0},
+dd4269[]={1251,0},
+dd4270[]={1253,0},
+dd4271[]={1257,0},
+dd4272[]={1263,0},
+dd4273[]={1265,0},
+dd4274[]={1267,0},
+dd4275[]={1271,0},
+dd4276[]={1275,0},
+dd4277[]={1277,0},
+dd4278[]={1279,0},
+dd4279[]={1281,0},
+dd4280[]={1283,0},
+dd4281[]={1285,0},
+dd4282[]={1287,0},
+dd4283[]={1289,0},
+dd4284[]={1291,0},
+dd4285[]={1293,0},
+dd4286[]={1295,0},
+dd4287[]={1297,0},
+dd4288[]={1299,0},
+dd4289[]={1301,0},
+dd4290[]={1303,0},
+dd4291[]={1305,0},
+dd4292[]={1307,0},
+dd4293[]={1309,0},
+dd4294[]={1311,0},
+dd4295[]={1313,0},
+dd4296[]={1315,0},
+dd4297[]={1377,0},
+dd4298[]={1378,0},
+dd4299[]={1379,0},
+dd4300[]={1380,0},
+dd4301[]={1381,0},
+dd4302[]={1382,0},
+dd4303[]={1383,0},
+dd4304[]={1384,0},
+dd4305[]={1385,0},
+dd4306[]={1386,0},
+dd4307[]={1387,0},
+dd4308[]={1388,0},
+dd4309[]={1389,0},
+dd4310[]={1390,0},
+dd4311[]={1391,0},
+dd4312[]={1392,0},
+dd4313[]={1393,0},
+dd4314[]={1394,0},
+dd4315[]={1395,0},
+dd4316[]={1396,0},
+dd4317[]={1397,0},
+dd4318[]={1398,0},
+dd4319[]={1399,0},
+dd4320[]={1400,0},
+dd4321[]={1401,0},
+dd4322[]={1402,0},
+dd4323[]={1403,0},
+dd4324[]={1404,0},
+dd4325[]={1405,0},
+dd4326[]={1406,0},
+dd4327[]={1407,0},
+dd4328[]={1408,0},
+dd4329[]={1409,0},
+dd4330[]={1410,0},
+dd4331[]={1411,0},
+dd4332[]={1412,0},
+dd4333[]={1413,0},
+dd4334[]={1414,0},
+dd4335[]={11520,0},
+dd4336[]={11521,0},
+dd4337[]={11522,0},
+dd4338[]={11523,0},
+dd4339[]={11524,0},
+dd4340[]={11525,0},
+dd4341[]={11526,0},
+dd4342[]={11527,0},
+dd4343[]={11528,0},
+dd4344[]={11529,0},
+dd4345[]={11530,0},
+dd4346[]={11531,0},
+dd4347[]={11532,0},
+dd4348[]={11533,0},
+dd4349[]={11534,0},
+dd4350[]={11535,0},
+dd4351[]={11536,0},
+dd4352[]={11537,0},
+dd4353[]={11538,0},
+dd4354[]={11539,0},
+dd4355[]={11540,0},
+dd4356[]={11541,0},
+dd4357[]={11542,0},
+dd4358[]={11543,0},
+dd4359[]={11544,0},
+dd4360[]={11545,0},
+dd4361[]={11546,0},
+dd4362[]={11547,0},
+dd4363[]={11548,0},
+dd4364[]={11549,0},
+dd4365[]={11550,0},
+dd4366[]={11551,0},
+dd4367[]={11552,0},
+dd4368[]={11553,0},
+dd4369[]={11554,0},
+dd4370[]={11555,0},
+dd4371[]={11556,0},
+dd4372[]={11557,0},
+dd4373[]={7681,0},
+dd4374[]={7683,0},
+dd4375[]={7685,0},
+dd4376[]={7687,0},
+dd4377[]={7691,0},
+dd4378[]={7693,0},
+dd4379[]={7695,0},
+dd4380[]={7697,0},
+dd4381[]={7699,0},
+dd4382[]={7701,0},
+dd4383[]={7703,0},
+dd4384[]={7705,0},
+dd4385[]={7707,0},
+dd4386[]={7713,0},
+dd4387[]={7715,0},
+dd4388[]={7717,0},
+dd4389[]={7719,0},
+dd4390[]={7721,0},
+dd4391[]={7723,0},
+dd4392[]={7725,0},
+dd4393[]={7729,0},
+dd4394[]={7731,0},
+dd4395[]={7733,0},
+dd4396[]={7735,0},
+dd4397[]={7739,0},
+dd4398[]={7741,0},
+dd4399[]={7743,0},
+dd4400[]={7745,0},
+dd4401[]={7747,0},
+dd4402[]={7749,0},
+dd4403[]={7751,0},
+dd4404[]={7753,0},
+dd4405[]={7755,0},
+dd4406[]={7757,0},
+dd4407[]={7759,0},
+dd4408[]={7761,0},
+dd4409[]={7763,0},
+dd4410[]={7765,0},
+dd4411[]={7767,0},
+dd4412[]={7769,0},
+dd4413[]={7771,0},
+dd4414[]={7775,0},
+dd4415[]={7777,0},
+dd4416[]={7779,0},
+dd4417[]={7787,0},
+dd4418[]={7789,0},
+dd4419[]={7791,0},
+dd4420[]={7793,0},
+dd4421[]={7795,0},
+dd4422[]={7797,0},
+dd4423[]={7799,0},
+dd4424[]={7805,0},
+dd4425[]={7807,0},
+dd4426[]={7809,0},
+dd4427[]={7811,0},
+dd4428[]={7813,0},
+dd4429[]={7815,0},
+dd4430[]={7817,0},
+dd4431[]={7819,0},
+dd4432[]={7821,0},
+dd4433[]={7823,0},
+dd4434[]={7825,0},
+dd4435[]={7827,0},
+dd4436[]={7829,0},
+dd4437[]={7841,0},
+dd4438[]={7843,0},
+dd4439[]={7845,0},
+dd4440[]={7847,0},
+dd4441[]={7849,0},
+dd4442[]={7851,0},
+dd4443[]={7853,0},
+dd4444[]={7855,0},
+dd4445[]={7857,0},
+dd4446[]={7859,0},
+dd4447[]={7861,0},
+dd4448[]={7863,0},
+dd4449[]={7865,0},
+dd4450[]={7867,0},
+dd4451[]={7869,0},
+dd4452[]={7871,0},
+dd4453[]={7873,0},
+dd4454[]={7875,0},
+dd4455[]={7877,0},
+dd4456[]={7881,0},
+dd4457[]={7883,0},
+dd4458[]={7885,0},
+dd4459[]={7887,0},
+dd4460[]={7889,0},
+dd4461[]={7891,0},
+dd4462[]={7893,0},
+dd4463[]={7895,0},
+dd4464[]={7899,0},
+dd4465[]={7901,0},
+dd4466[]={7903,0},
+dd4467[]={7905,0},
+dd4468[]={7907,0},
+dd4469[]={7909,0},
+dd4470[]={7911,0},
+dd4471[]={7913,0},
+dd4472[]={7915,0},
+dd4473[]={7917,0},
+dd4474[]={7919,0},
+dd4475[]={7921,0},
+dd4476[]={7923,0},
+dd4477[]={7925,0},
+dd4478[]={7927,0},
+dd4479[]={7929,0},
+dd4480[]={7931,0},
+dd4481[]={7933,0},
+dd4482[]={7935,0},
+dd4483[]={7936,0},
+dd4484[]={7937,0},
+dd4485[]={7938,0},
+dd4486[]={7939,0},
+dd4487[]={7940,0},
+dd4488[]={7941,0},
+dd4489[]={7942,0},
+dd4490[]={7943,0},
+dd4491[]={7952,0},
+dd4492[]={7953,0},
+dd4493[]={7954,0},
+dd4494[]={7955,0},
+dd4495[]={7956,0},
+dd4496[]={7957,0},
+dd4497[]={7968,0},
+dd4498[]={7969,0},
+dd4499[]={7970,0},
+dd4500[]={7971,0},
+dd4501[]={7972,0},
+dd4502[]={7973,0},
+dd4503[]={7974,0},
+dd4504[]={7975,0},
+dd4505[]={7984,0},
+dd4506[]={7985,0},
+dd4507[]={7986,0},
+dd4508[]={7987,0},
+dd4509[]={7988,0},
+dd4510[]={7989,0},
+dd4511[]={7990,0},
+dd4512[]={7991,0},
+dd4513[]={8000,0},
+dd4514[]={8001,0},
+dd4515[]={8002,0},
+dd4516[]={8003,0},
+dd4517[]={8004,0},
+dd4518[]={8005,0},
+dd4519[]={965,787,768,0},
+dd4520[]={965,787,769,0},
+dd4521[]={965,787,834,0},
+dd4522[]={8017,0},
+dd4523[]={8019,0},
+dd4524[]={8021,0},
+dd4525[]={8023,0},
+dd4526[]={8032,0},
+dd4527[]={8033,0},
+dd4528[]={8034,0},
+dd4529[]={8035,0},
+dd4530[]={8036,0},
+dd4531[]={8037,0},
+dd4532[]={8038,0},
+dd4533[]={8039,0},
+dd4534[]={7936,953,0},
+dd4535[]={7937,953,0},
+dd4536[]={7938,953,0},
+dd4537[]={7939,953,0},
+dd4538[]={7940,953,0},
+dd4539[]={7941,953,0},
+dd4540[]={7942,953,0},
+dd4541[]={7943,953,0},
+dd4542[]={7968,953,0},
+dd4543[]={7969,953,0},
+dd4544[]={7970,953,0},
+dd4545[]={7971,953,0},
+dd4546[]={7972,953,0},
+dd4547[]={7973,953,0},
+dd4548[]={7974,953,0},
+dd4549[]={7975,953,0},
+dd4550[]={8032,953,0},
+dd4551[]={8033,953,0},
+dd4552[]={8034,953,0},
+dd4553[]={8035,953,0},
+dd4554[]={8036,953,0},
+dd4555[]={8037,953,0},
+dd4556[]={8038,953,0},
+dd4557[]={8039,953,0},
+dd4558[]={8048,953,0},
+dd4559[]={945,953,0},
+dd4560[]={940,953,0},
+dd4561[]={945,834,953,0},
+dd4562[]={8112,0},
+dd4563[]={8113,0},
+dd4564[]={8048,0},
+dd4565[]={8049,0},
+dd4566[]={8052,953,0},
+dd4567[]={951,953,0},
+dd4568[]={942,953,0},
+dd4569[]={951,834,953,0},
+dd4570[]={8050,0},
+dd4571[]={8051,0},
+dd4572[]={8052,0},
+dd4573[]={8053,0},
+dd4574[]={953,776,768,0},
+dd4575[]={953,776,834,0},
+dd4576[]={8144,0},
+dd4577[]={8145,0},
+dd4578[]={8054,0},
+dd4579[]={8055,0},
+dd4580[]={965,776,768,0},
+dd4581[]={965,776,834,0},
+dd4582[]={8160,0},
+dd4583[]={8161,0},
+dd4584[]={8058,0},
+dd4585[]={8059,0},
+dd4586[]={8165,0},
+dd4587[]={8060,953,0},
+dd4588[]={969,953,0},
+dd4589[]={974,953,0},
+dd4590[]={969,834,953,0},
+dd4591[]={8056,0},
+dd4592[]={8057,0},
+dd4593[]={8060,0},
+dd4594[]={8061,0},
+dd4595[]={8526,0},
+dd4596[]={8560,0},
+dd4597[]={8561,0},
+dd4598[]={8562,0},
+dd4599[]={8563,0},
+dd4600[]={8564,0},
+dd4601[]={8565,0},
+dd4602[]={8566,0},
+dd4603[]={8567,0},
+dd4604[]={8568,0},
+dd4605[]={8569,0},
+dd4606[]={8570,0},
+dd4607[]={8571,0},
+dd4608[]={8572,0},
+dd4609[]={8573,0},
+dd4610[]={8574,0},
+dd4611[]={8575,0},
+dd4612[]={8580,0},
+dd4613[]={9424,0},
+dd4614[]={9425,0},
+dd4615[]={9426,0},
+dd4616[]={9427,0},
+dd4617[]={9428,0},
+dd4618[]={9429,0},
+dd4619[]={9430,0},
+dd4620[]={9431,0},
+dd4621[]={9432,0},
+dd4622[]={9433,0},
+dd4623[]={9434,0},
+dd4624[]={9435,0},
+dd4625[]={9436,0},
+dd4626[]={9437,0},
+dd4627[]={9438,0},
+dd4628[]={9439,0},
+dd4629[]={9440,0},
+dd4630[]={9441,0},
+dd4631[]={9442,0},
+dd4632[]={9443,0},
+dd4633[]={9444,0},
+dd4634[]={9445,0},
+dd4635[]={9446,0},
+dd4636[]={9447,0},
+dd4637[]={9448,0},
+dd4638[]={9449,0},
+dd4639[]={11312,0},
+dd4640[]={11313,0},
+dd4641[]={11314,0},
+dd4642[]={11315,0},
+dd4643[]={11316,0},
+dd4644[]={11317,0},
+dd4645[]={11318,0},
+dd4646[]={11319,0},
+dd4647[]={11320,0},
+dd4648[]={11321,0},
+dd4649[]={11322,0},
+dd4650[]={11323,0},
+dd4651[]={11324,0},
+dd4652[]={11325,0},
+dd4653[]={11326,0},
+dd4654[]={11327,0},
+dd4655[]={11328,0},
+dd4656[]={11329,0},
+dd4657[]={11330,0},
+dd4658[]={11331,0},
+dd4659[]={11332,0},
+dd4660[]={11333,0},
+dd4661[]={11334,0},
+dd4662[]={11335,0},
+dd4663[]={11336,0},
+dd4664[]={11337,0},
+dd4665[]={11338,0},
+dd4666[]={11339,0},
+dd4667[]={11340,0},
+dd4668[]={11341,0},
+dd4669[]={11342,0},
+dd4670[]={11343,0},
+dd4671[]={11344,0},
+dd4672[]={11345,0},
+dd4673[]={11346,0},
+dd4674[]={11347,0},
+dd4675[]={11348,0},
+dd4676[]={11349,0},
+dd4677[]={11350,0},
+dd4678[]={11351,0},
+dd4679[]={11352,0},
+dd4680[]={11353,0},
+dd4681[]={11354,0},
+dd4682[]={11355,0},
+dd4683[]={11356,0},
+dd4684[]={11357,0},
+dd4685[]={11358,0},
+dd4686[]={11361,0},
+dd4687[]={619,0},
+dd4688[]={7549,0},
+dd4689[]={637,0},
+dd4690[]={11368,0},
+dd4691[]={11370,0},
+dd4692[]={11372,0},
+dd4693[]={11379,0},
+dd4694[]={11382,0},
+dd4695[]={11393,0},
+dd4696[]={11395,0},
+dd4697[]={11397,0},
+dd4698[]={11399,0},
+dd4699[]={11401,0},
+dd4700[]={11403,0},
+dd4701[]={11405,0},
+dd4702[]={11407,0},
+dd4703[]={11409,0},
+dd4704[]={11411,0},
+dd4705[]={11413,0},
+dd4706[]={11415,0},
+dd4707[]={11417,0},
+dd4708[]={11419,0},
+dd4709[]={11421,0},
+dd4710[]={11423,0},
+dd4711[]={11425,0},
+dd4712[]={11427,0},
+dd4713[]={11429,0},
+dd4714[]={11431,0},
+dd4715[]={11433,0},
+dd4716[]={11435,0},
+dd4717[]={11437,0},
+dd4718[]={11439,0},
+dd4719[]={11441,0},
+dd4720[]={11443,0},
+dd4721[]={11445,0},
+dd4722[]={11447,0},
+dd4723[]={11449,0},
+dd4724[]={11451,0},
+dd4725[]={11453,0},
+dd4726[]={11455,0},
+dd4727[]={11457,0},
+dd4728[]={11459,0},
+dd4729[]={11461,0},
+dd4730[]={11463,0},
+dd4731[]={11465,0},
+dd4732[]={11467,0},
+dd4733[]={11469,0},
+dd4734[]={11471,0},
+dd4735[]={11473,0},
+dd4736[]={11475,0},
+dd4737[]={11477,0},
+dd4738[]={11479,0},
+dd4739[]={11481,0},
+dd4740[]={11483,0},
+dd4741[]={11485,0},
+dd4742[]={11487,0},
+dd4743[]={11489,0},
+dd4744[]={11491,0},
+dd4745[]={42561,0},
+dd4746[]={42563,0},
+dd4747[]={42565,0},
+dd4748[]={42567,0},
+dd4749[]={42569,0},
+dd4750[]={42571,0},
+dd4751[]={42573,0},
+dd4752[]={42575,0},
+dd4753[]={42577,0},
+dd4754[]={42579,0},
+dd4755[]={42581,0},
+dd4756[]={42583,0},
+dd4757[]={42585,0},
+dd4758[]={42587,0},
+dd4759[]={42589,0},
+dd4760[]={42591,0},
+dd4761[]={42595,0},
+dd4762[]={42597,0},
+dd4763[]={42599,0},
+dd4764[]={42601,0},
+dd4765[]={42603,0},
+dd4766[]={42605,0},
+dd4767[]={42625,0},
+dd4768[]={42627,0},
+dd4769[]={42629,0},
+dd4770[]={42631,0},
+dd4771[]={42633,0},
+dd4772[]={42635,0},
+dd4773[]={42637,0},
+dd4774[]={42639,0},
+dd4775[]={42641,0},
+dd4776[]={42643,0},
+dd4777[]={42645,0},
+dd4778[]={42647,0},
+dd4779[]={42787,0},
+dd4780[]={42789,0},
+dd4781[]={42791,0},
+dd4782[]={42793,0},
+dd4783[]={42795,0},
+dd4784[]={42797,0},
+dd4785[]={42799,0},
+dd4786[]={42803,0},
+dd4787[]={42805,0},
+dd4788[]={42807,0},
+dd4789[]={42809,0},
+dd4790[]={42811,0},
+dd4791[]={42813,0},
+dd4792[]={42815,0},
+dd4793[]={42817,0},
+dd4794[]={42819,0},
+dd4795[]={42821,0},
+dd4796[]={42823,0},
+dd4797[]={42825,0},
+dd4798[]={42827,0},
+dd4799[]={42829,0},
+dd4800[]={42831,0},
+dd4801[]={42833,0},
+dd4802[]={42835,0},
+dd4803[]={42837,0},
+dd4804[]={42839,0},
+dd4805[]={42841,0},
+dd4806[]={42843,0},
+dd4807[]={42845,0},
+dd4808[]={42847,0},
+dd4809[]={42849,0},
+dd4810[]={42851,0},
+dd4811[]={42853,0},
+dd4812[]={42855,0},
+dd4813[]={42857,0},
+dd4814[]={42859,0},
+dd4815[]={42861,0},
+dd4816[]={42874,0},
+dd4817[]={42876,0},
+dd4818[]={7545,0},
+dd4819[]={42879,0},
+dd4820[]={42881,0},
+dd4821[]={42883,0},
+dd4822[]={42885,0},
+dd4823[]={42887,0},
+dd4824[]={42892,0},
+dd4825[]={65345,0},
+dd4826[]={65346,0},
+dd4827[]={65347,0},
+dd4828[]={65348,0},
+dd4829[]={65349,0},
+dd4830[]={65350,0},
+dd4831[]={65351,0},
+dd4832[]={65352,0},
+dd4833[]={65353,0},
+dd4834[]={65354,0},
+dd4835[]={65355,0},
+dd4836[]={65356,0},
+dd4837[]={65357,0},
+dd4838[]={65358,0},
+dd4839[]={65359,0},
+dd4840[]={65360,0},
+dd4841[]={65361,0},
+dd4842[]={65362,0},
+dd4843[]={65363,0},
+dd4844[]={65364,0},
+dd4845[]={65365,0},
+dd4846[]={65366,0},
+dd4847[]={65367,0},
+dd4848[]={65368,0},
+dd4849[]={65369,0},
+dd4850[]={65370,0},
+dd4851[]={66600,0},
+dd4852[]={66601,0},
+dd4853[]={66602,0},
+dd4854[]={66603,0},
+dd4855[]={66604,0},
+dd4856[]={66605,0},
+dd4857[]={66606,0},
+dd4858[]={66607,0},
+dd4859[]={66608,0},
+dd4860[]={66609,0},
+dd4861[]={66610,0},
+dd4862[]={66611,0},
+dd4863[]={66612,0},
+dd4864[]={66613,0},
+dd4865[]={66614,0},
+dd4866[]={66615,0},
+dd4867[]={66616,0},
+dd4868[]={66617,0},
+dd4869[]={66618,0},
+dd4870[]={66619,0},
+dd4871[]={66620,0},
+dd4872[]={66621,0},
+dd4873[]={66622,0},
+dd4874[]={66623,0},
+dd4875[]={66624,0},
+dd4876[]={66625,0},
+dd4877[]={66626,0},
+dd4878[]={66627,0},
+dd4879[]={66628,0},
+dd4880[]={66629,0},
+dd4881[]={66630,0},
+dd4882[]={66631,0},
+dd4883[]={66632,0},
+dd4884[]={66633,0},
+dd4885[]={66634,0},
+dd4886[]={66635,0},
+dd4887[]={66636,0},
+dd4888[]={66637,0},
+dd4889[]={66638,0},
+dd4890[]={66639,0};
 /* 0000-000F */
 static const struct unidata st0[] = {
 {0,0,0,0,Cc,0,GBControl,WBOther,SBOther},
@@ -4913,10 +5028,10 @@ static const struct unidata st0[] = {
 {0,0,0,0,Cc,0,GBControl,WBOther,SBOther},
 {0,0,0,0,Cc,0,GBControl,WBOther,SBOther},
 {0,0,0,0,Cc,0,GBControl,WBOther,SBSp},
-{0,0,0,0,Cc,0,GBLF,WBOther,SBSep},
-{0,0,0,0,Cc,0,GBControl,WBOther,SBSp},
-{0,0,0,0,Cc,0,GBControl,WBOther,SBSp},
-{0,0,0,0,Cc,0,GBCR,WBOther,SBSep},
+{0,0,0,0,Cc,0,GBLF,WBLF,SBLF},
+{0,0,0,0,Cc,0,GBControl,WBNewline,SBSp},
+{0,0,0,0,Cc,0,GBControl,WBNewline,SBSp},
+{0,0,0,0,Cc,0,GBCR,WBCR,SBCR},
 {0,0,0,0,Cc,0,GBControl,WBOther,SBOther},
 {0,0,0,0,Cc,0,GBControl,WBOther,SBOther}
 };
@@ -4948,14 +5063,14 @@ static const struct unidata st2[] = {
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBMidLetter,SBClose},
+{0,0,0,0,Po,0,GBOther,WBMidNumLet,SBClose},
 {0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBMidNum,SBOther},
-{0,0,0,0,Pd,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBMidNum,SBATerm},
+{0,0,0,0,Po,0,GBOther,WBMidNum,SBSContinue},
+{0,0,0,0,Pd,0,GBOther,WBOther,SBSContinue},
+{0,0,0,0,Po,0,GBOther,WBMidNumLet,SBATerm},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
 /* 0030-003F */
@@ -4970,45 +5085,45 @@ static const struct unidata st3[] = {
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
-{0,0,0,0,Po,0,GBOther,WBMidLetter,SBOther},
+{0,0,0,0,Po,0,GBOther,WBMidLetter,SBSContinue},
 {0,0,0,0,Po,0,GBOther,WBMidNum,SBOther},
-{0,0,dd3607,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3608,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3609,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3610,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBSTerm}
 };
 /* 0040-004F */
 static const struct unidata st4[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,dd2,dd3610,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd515,dd3611,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd541,dd3612,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd516,dd3613,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd517,dd3614,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd544,dd3615,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd521,dd3616,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd284,dd3617,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd538,dd3618,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd286,dd3619,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd522,dd3620,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd300,dd3621,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd523,dd3622,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd1082,dd3623,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd10,dd3624,0,Lu,0,GBOther,WBALetter,SBUpper}
+{0,dd2,dd3611,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd515,dd3612,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd541,dd3613,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd516,dd3614,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd517,dd3615,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd544,dd3616,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd521,dd3617,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd284,dd3618,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd538,dd3619,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd286,dd3620,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd522,dd3621,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd300,dd3622,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd523,dd3623,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd1082,dd3624,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd10,dd3625,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 0050-005F */
 static const struct unidata st5[] = {
-{0,dd528,dd3625,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd528,dd3626,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd1272,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd287,dd3626,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd180,dd3627,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd529,dd3628,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd530,dd3629,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd533,dd3630,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd291,dd3631,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd301,dd3632,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd292,dd3633,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd571,dd3634,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd287,dd3627,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd180,dd3628,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd529,dd3629,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd530,dd3630,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd533,dd3631,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd291,dd3632,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd301,dd3633,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd292,dd3634,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd571,dd3635,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
@@ -5018,7 +5133,6 @@ static const struct unidata st5[] = {
 /* 0060-006F */
 static const struct unidata st6[] = {
 {0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,dd3635,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3636,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3637,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3638,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -5032,13 +5146,13 @@ static const struct unidata st6[] = {
 {0,0,dd3646,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3647,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3648,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,dd3649,0,Ll,0,GBOther,WBALetter,SBLower}
+{0,0,dd3649,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3650,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0070-007F */
 static const struct unidata st7[] = {
-{0,0,dd3650,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3651,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3652,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3653,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3654,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -5047,6 +5161,7 @@ static const struct unidata st7[] = {
 {0,0,dd3657,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3658,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3659,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3660,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
@@ -5060,7 +5175,7 @@ static const struct unidata st8[] = {
 {0,0,0,0,Cc,0,GBControl,WBOther,SBOther},
 {0,0,0,0,Cc,0,GBControl,WBOther,SBOther},
 {0,0,0,0,Cc,0,GBControl,WBOther,SBOther},
-{0,0,0,0,Cc,0,GBControl,WBOther,SBSep},
+{0,0,0,0,Cc,0,GBControl,WBNewline,SBSep},
 {0,0,0,0,Cc,0,GBControl,WBOther,SBOther},
 {0,0,0,0,Cc,0,GBControl,WBOther,SBOther},
 {0,0,0,0,Cc,0,GBControl,WBOther,SBOther},
@@ -5074,7 +5189,7 @@ static const struct unidata st8[] = {
 };
 /* 00A0-00AF */
 static const struct unidata st9[] = {
-{dd0,0,0,0,Zs,CD,GBOther,WBOther,SBOLetter},
+{dd0,0,0,0,Zs,CD,GBOther,WBOther,SBSp},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
@@ -5082,7 +5197,7 @@ static const struct unidata st9[] = {
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd1,0,dd3660,0,Sk,CD,GBOther,WBOther,SBOther},
+{dd1,0,dd3661,0,Sk,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {0,0,0,0,Pi,0,GBOther,WBOther,SBClose},
@@ -5112,60 +5227,60 @@ static const struct unidata st10[] = {
 };
 /* 00C0-00CF */
 static const struct unidata st11[] = {
-{dd14,dd3970,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd15,dd3971,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd16,dd3972,dd3661,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd17,dd3973,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd18,dd3974,dd3662,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd19,dd3975,dd3663,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd3976,dd3664,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd20,dd3977,dd3665,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd21,dd3978,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd22,dd3979,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd23,dd3980,dd3666,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd24,dd3981,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd25,dd3982,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd26,dd3983,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd27,dd3984,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd28,dd3985,dd3667,0,Lu,0,GBOther,WBALetter,SBUpper}
+{dd14,dd3971,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd15,dd3972,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd16,dd3973,dd3662,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd17,dd3974,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd18,dd3975,dd3663,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd19,dd3976,dd3664,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd3977,dd3665,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd20,dd3978,dd3666,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd21,dd3979,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd22,dd3980,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd23,dd3981,dd3667,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd24,dd3982,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd25,dd3983,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd26,dd3984,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd27,dd3985,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd28,dd3986,dd3668,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 00D0-00DF */
 static const struct unidata st12[] = {
 {0,dd543,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd29,dd3986,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd30,dd3987,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd31,dd3988,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd32,dd3989,dd3668,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd33,dd3990,dd3669,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd34,dd3991,dd3670,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,dd3992,dd3671,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd35,dd3993,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd36,dd3994,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd37,dd3995,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd38,dd3996,dd3672,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd39,dd3997,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd3998,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd3999,0,0,Ll,0,GBOther,WBALetter,SBLower}
+{dd29,dd3987,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd30,dd3988,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd31,dd3989,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd32,dd3990,dd3669,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd33,dd3991,dd3670,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd34,dd3992,dd3671,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,dd3993,dd3672,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd35,dd3994,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd36,dd3995,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd37,dd3996,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd38,dd3997,dd3673,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd39,dd3998,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd3999,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4000,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 00E0-00EF */
 static const struct unidata st13[] = {
 {dd40,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd41,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd42,0,dd3673,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd42,0,dd3674,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd43,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd44,0,dd3674,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd45,0,dd3675,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,dd3676,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd46,0,dd3677,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd44,0,dd3675,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd45,0,dd3676,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3677,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd46,0,dd3678,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd47,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd48,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd49,0,dd3678,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd49,0,dd3679,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd50,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd51,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd52,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd53,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd54,0,dd3679,0,Ll,0,GBOther,WBALetter,SBLower}
+{dd54,0,dd3680,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 00F0-00FF */
 static const struct unidata st14[] = {
@@ -5173,201 +5288,201 @@ static const struct unidata st14[] = {
 {dd55,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd56,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd57,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd58,0,dd3680,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd59,0,dd3681,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd60,0,dd3682,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd58,0,dd3681,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd59,0,dd3682,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd60,0,dd3683,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3683,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3684,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd61,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd62,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd63,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd64,0,dd3684,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd64,0,dd3685,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd65,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd66,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0100-010F */
 static const struct unidata st15[] = {
-{dd67,dd4000,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd67,dd4001,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd68,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd69,dd4001,dd3685,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd70,0,dd3686,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd71,dd4002,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd69,dd4002,dd3686,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd70,0,dd3687,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd71,dd4003,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd72,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd73,dd4003,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd73,dd4004,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd74,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd75,dd4004,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd75,dd4005,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd76,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd77,dd4005,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd77,dd4006,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd78,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd79,dd4006,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd79,dd4007,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd80,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd81,dd4007,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd81,dd4008,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd82,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0110-011F */
 static const struct unidata st16[] = {
-{0,dd4008,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4009,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd83,dd4009,dd3687,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd84,0,dd3688,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd85,dd4010,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd83,dd4010,dd3688,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd84,0,dd3689,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd85,dd4011,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd86,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd87,dd4011,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd87,dd4012,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd88,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd89,dd4012,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd89,dd4013,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd90,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd91,dd4013,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd91,dd4014,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd92,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd93,dd4014,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd93,dd4015,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd94,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd95,dd4015,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd95,dd4016,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd96,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0120-012F */
 static const struct unidata st17[] = {
-{dd97,dd4016,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd97,dd4017,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd98,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd99,dd4017,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd99,dd4018,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd100,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd101,dd4018,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd101,dd4019,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd102,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd1092,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd103,dd4019,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd103,dd4020,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd104,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd105,dd4020,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd105,dd4021,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd106,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd107,dd4021,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd107,dd4022,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd108,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd109,dd4022,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd109,dd4023,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd110,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0130-013F */
 static const struct unidata st18[] = {
-{dd111,dd4023,0,0,Lu,NBC,GBOther,WBALetter,SBUpper},
+{dd111,dd4024,0,0,Lu,NBC,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd112,dd4024,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd112,dd4025,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {dd113,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd114,dd4025,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd114,dd4026,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd115,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd116,dd4026,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd116,dd4027,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd117,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd118,dd4027,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd118,dd4028,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd119,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd120,dd4028,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd120,dd4029,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd121,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd122,dd4029,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd122,dd4030,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd123,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd124,dd4030,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{dd124,dd4031,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
 /* 0140-014F */
 static const struct unidata st19[] = {
 {dd125,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{0,dd4031,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4032,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd126,dd4032,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd126,dd4033,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd127,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd128,dd4033,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd128,dd4034,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd129,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd130,dd4034,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd130,dd4035,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd131,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd132,dd132,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {0,dd524,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd133,dd4035,dd3689,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd134,0,dd3690,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd135,dd4036,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd133,dd4036,dd3690,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd134,0,dd3691,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd135,dd4037,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd136,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0150-015F */
 static const struct unidata st20[] = {
-{dd137,dd4037,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd137,dd4038,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd138,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4038,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4039,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd139,dd4039,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd139,dd4040,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd140,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd141,dd4040,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd141,dd4041,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd142,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd143,dd4041,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd143,dd4042,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd144,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd145,dd4042,dd3691,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd146,0,dd3692,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd147,dd4043,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd145,dd4043,dd3692,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd146,0,dd3693,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd147,dd4044,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd148,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd149,dd4044,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd149,dd4045,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd150,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0160-016F */
 static const struct unidata st21[] = {
-{dd151,dd4045,dd3693,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd152,0,dd3694,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd153,dd4046,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd151,dd4046,dd3694,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd152,0,dd3695,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd153,dd4047,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd154,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd155,dd4047,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd155,dd4048,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd156,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4048,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4049,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd157,dd4049,dd3695,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd158,0,dd3696,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd159,dd4050,dd3697,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd160,0,dd3698,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd161,dd4051,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd157,dd4050,dd3696,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd158,0,dd3697,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd159,dd4051,dd3698,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd160,0,dd3699,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd161,dd4052,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd162,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd163,dd4052,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd163,dd4053,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd164,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0170-017F */
 static const struct unidata st22[] = {
-{dd165,dd4053,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd165,dd4054,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd166,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd167,dd4054,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd167,dd4055,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd168,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd169,dd4055,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd169,dd4056,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd170,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd171,dd4056,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd171,dd4057,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd172,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd173,dd4057,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd174,dd4058,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd173,dd4058,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd174,dd4059,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd175,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd176,dd4059,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd176,dd4060,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd177,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd178,dd4060,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd178,dd4061,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd179,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd180,dd180,dd3699,0,Ll,CD,GBOther,WBALetter,SBLower}
+{dd180,dd180,dd3700,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
 /* 0180-018F */
 static const struct unidata st23[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4061,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4062,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4063,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd525,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4064,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd525,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4065,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4066,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4067,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4068,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4068,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4069,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd518,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 0190-019F */
 static const struct unidata st24[] = {
 {0,dd519,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4069,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4070,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4071,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd299,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd549,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd548,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4071,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4072,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -5378,38 +5493,38 @@ static const struct unidata st24[] = {
 };
 /* 01A0-01AF */
 static const struct unidata st25[] = {
-{dd181,dd4072,dd3700,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd182,0,dd3701,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4073,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd181,dd4073,dd3701,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd182,0,dd3702,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4074,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4075,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4076,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4077,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd564,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4077,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4078,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd183,dd4079,dd3702,0,Lu,0,GBOther,WBALetter,SBUpper}
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4079,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd183,dd4080,dd3703,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 01B0-01BF */
 static const struct unidata st26[] = {
-{dd184,0,dd3703,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd184,0,dd3704,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd567,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd569,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4080,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4081,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd574,dd3704,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4082,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd574,dd3705,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4083,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,dd4083,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4084,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
@@ -5420,138 +5535,138 @@ static const struct unidata st27[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd185,dd4084,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd186,dd4084,0,0,Lt,CD,GBOther,WBALetter,SBUpper},
+{dd185,dd4085,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd186,dd4085,0,0,Lt,CD,GBOther,WBALetter,SBUpper},
 {dd187,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd188,dd4085,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd189,dd4085,0,0,Lt,CD,GBOther,WBALetter,SBUpper},
+{dd188,dd4086,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd189,dd4086,0,0,Lt,CD,GBOther,WBALetter,SBUpper},
 {dd190,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd191,dd4086,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd192,dd4086,0,0,Lt,CD,GBOther,WBALetter,SBUpper},
+{dd191,dd4087,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd192,dd4087,0,0,Lt,CD,GBOther,WBALetter,SBUpper},
 {dd193,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd194,dd4087,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd194,dd4088,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd195,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd196,dd4088,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+{dd196,dd4089,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 01D0-01DF */
 static const struct unidata st28[] = {
 {dd197,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd198,dd4089,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd198,dd4090,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd199,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd200,dd4090,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd200,dd4091,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd201,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd202,dd4091,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd202,dd4092,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd203,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd204,dd4092,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd204,dd4093,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd205,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd206,dd4093,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd206,dd4094,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd207,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd208,dd4094,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd208,dd4095,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd209,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd210,dd3674,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd210,dd3675,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd211,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 01E0-01EF */
 static const struct unidata st29[] = {
-{dd212,dd3708,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd212,dd3709,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd213,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd214,dd4095,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd214,dd4096,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd215,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4096,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4097,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd216,dd4097,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd216,dd4098,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd217,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd218,dd4098,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd218,dd4099,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd219,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd220,dd4099,dd3705,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd221,0,dd3706,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd222,dd3706,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd220,dd4100,dd3706,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd221,0,dd3707,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd222,dd3707,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd223,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd224,dd3713,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd224,dd3714,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd225,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 01F0-01FF */
 static const struct unidata st30[] = {
 {dd226,dd226,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd227,dd4100,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd228,dd4100,0,0,Lt,CD,GBOther,WBALetter,SBUpper},
+{dd227,dd4101,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd228,dd4101,0,0,Lt,CD,GBOther,WBALetter,SBUpper},
 {dd229,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd230,dd4101,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd230,dd4102,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd231,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4102,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4103,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd232,dd4104,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4104,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd232,dd4105,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd233,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd234,dd3675,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd234,dd3676,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd235,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd236,dd4105,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd236,dd4106,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd237,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd238,dd3683,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd238,dd3684,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd239,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0200-020F */
 static const struct unidata st31[] = {
-{dd240,dd4106,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd240,dd4107,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd241,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd242,dd4107,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd242,dd4108,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd243,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd244,dd4108,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd244,dd4109,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd245,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd246,dd4109,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd246,dd4110,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd247,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd248,dd4110,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd248,dd4111,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd249,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd250,dd4111,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd250,dd4112,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd251,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd252,dd4112,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd252,dd4113,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd253,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd254,dd4113,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd254,dd4114,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd255,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0210-021F */
 static const struct unidata st32[] = {
-{dd256,dd4114,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd256,dd4115,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd257,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd258,dd4115,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd258,dd4116,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd259,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd260,dd4116,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd260,dd4117,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd261,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd262,dd4117,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd262,dd4118,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd263,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd264,dd4118,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd264,dd4119,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd265,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd266,dd4119,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd266,dd4120,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd267,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4120,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4121,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd268,dd4121,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd268,dd4122,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd269,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0220-022F */
 static const struct unidata st33[] = {
-{0,dd4122,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4123,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4124,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd270,dd4125,dd3707,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd271,0,dd3708,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd272,dd4126,dd3709,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd273,0,dd3710,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd274,dd3682,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4125,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd270,dd4126,dd3708,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd271,0,dd3709,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd272,dd4127,dd3710,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd273,0,dd3711,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd274,dd3683,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd275,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd276,dd4127,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd276,dd4128,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd277,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd278,dd4128,dd3711,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd279,0,dd3712,0,Ll,0,GBOther,WBALetter,SBLower}
+{dd278,dd4129,dd3712,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd279,0,dd3713,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0230-023F */
 static const struct unidata st34[] = {
-{dd280,dd3712,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd280,dd3713,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd281,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd282,dd4129,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd282,dd4130,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd283,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -5559,23 +5674,21 @@ static const struct unidata st34[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4130,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4131,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4132,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4133,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4134,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0240-024F */
 static const struct unidata st35[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4134,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4135,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4136,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd566,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd570,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4136,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4137,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4138,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -5583,6 +5696,8 @@ static const struct unidata st35[] = {
 {0,dd4139,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4140,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4141,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0250-025F */
@@ -5608,7 +5723,7 @@ static const struct unidata st36[] = {
 static const struct unidata st37[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,dd3713,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3714,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -5694,7 +5809,7 @@ static const struct unidata st41[] = {
 {0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Sk,0,GBOther,WBOther,SBOther}
@@ -5853,14 +5968,14 @@ static const struct unidata st49[] = {
 };
 /* 0370-037F */
 static const struct unidata st50[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd307,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,dd4142,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4143,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd307,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,dd4144,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {dd308,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
@@ -5879,7 +5994,7 @@ static const struct unidata st51[] = {
 {dd6,0,0,0,Sk,CD,GBOther,WBOther,SBOther},
 {dd310,0,0,0,Sk,0,GBOther,WBOther,SBOther},
 {dd311,dd922,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd312,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{dd312,0,0,0,Po,0,GBOther,WBMidLetter,SBOther},
 {dd313,dd924,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd314,dd926,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd315,dd928,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -5891,116 +6006,116 @@ static const struct unidata st51[] = {
 };
 /* 0390-039F */
 static const struct unidata st52[] = {
-{dd319,dd4141,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{0,dd3117,dd3714,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd319,dd4145,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{0,dd3118,dd3715,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd332,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd535,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd536,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd343,dd3715,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd3118,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd3119,dd3716,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd343,dd3716,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd3119,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd3120,dd3717,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd333,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd996,dd3717,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd996,dd3718,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd339,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd3120,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd7,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd3121,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd7,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd3122,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd3123,dd3718,0,Lu,0,GBOther,WBALetter,SBUpper}
+{0,dd3123,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd3124,dd3719,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 03A0-03AF */
 static const struct unidata st53[] = {
 {0,dd338,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd340,dd3719,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd340,dd3720,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,dd3124,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd3125,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd3126,dd3720,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd3126,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd3127,dd3721,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd337,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd537,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd3127,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd3128,dd3721,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd320,dd4142,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd321,dd4143,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd322,0,dd3722,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd3128,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd3129,dd3722,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd320,dd4146,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd321,dd4147,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd322,0,dd3723,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd323,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd324,0,dd3723,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd324,0,dd3724,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd325,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 03B0-03BF */
 static const struct unidata st54[] = {
-{dd326,dd4144,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{0,0,dd3724,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd326,dd4148,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{0,0,dd3725,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,dd3725,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3726,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3727,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3728,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,dd3728,0,Ll,0,GBOther,WBALetter,SBLower}
+{0,0,dd3729,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 03C0-03CF */
 static const struct unidata st55[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,dd3729,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd3124,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3730,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd3125,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,dd3730,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3731,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,dd3731,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd327,0,dd3732,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd328,0,dd3733,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3732,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd327,0,dd3733,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd328,0,dd3734,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd329,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd330,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd331,0,dd3734,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd331,0,dd3735,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4149,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 03D0-03DF */
 static const struct unidata st56[] = {
 {dd332,dd332,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {dd333,dd333,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd334,0,dd3735,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd334,0,dd3736,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {dd335,0,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd336,0,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd337,dd337,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {dd338,dd338,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4145,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4150,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4146,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4151,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd3137,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd3138,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4147,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4152,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 03E0-03EF */
 static const struct unidata st57[] = {
-{0,dd4148,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4153,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4149,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4154,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4150,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4155,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4151,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4156,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4152,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4157,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4153,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4158,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4154,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4159,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4155,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4160,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 03F0-03FF */
@@ -6012,90 +6127,90 @@ static const struct unidata st58[] = {
 {dd342,dd333,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {dd343,dd343,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,dd4156,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4161,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd344,dd4157,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{0,dd4158,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd344,dd4162,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,dd4163,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4159,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4160,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4161,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+{0,dd4164,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4165,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4166,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 0400-040F */
 static const struct unidata st59[] = {
-{dd345,dd4162,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd346,dd4163,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4164,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd347,dd3750,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4165,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4166,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4167,dd3736,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd348,dd3761,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4168,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd345,dd4167,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd346,dd4168,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4169,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd347,dd3751,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4170,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4171,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd349,dd3755,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd350,dd4172,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd351,dd4173,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4174,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+{0,dd4172,dd3737,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd348,dd3762,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4173,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4174,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4175,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4176,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd349,dd3756,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd350,dd4177,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd351,dd4178,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4179,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 0410-041F */
 static const struct unidata st60[] = {
-{0,dd4175,dd3737,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4176,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4177,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4178,dd3738,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4179,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4180,dd3739,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4181,dd3740,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4182,dd3741,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4183,dd3742,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd352,dd4184,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4185,dd3743,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4186,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4187,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4180,dd3738,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4181,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4182,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4183,dd3739,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4184,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4185,dd3740,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4186,dd3741,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4187,dd3742,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4188,dd3743,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd352,dd4189,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4190,dd3744,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4191,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4192,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd539,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4188,dd3744,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4189,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+{0,dd4193,dd3745,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4194,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 0420-042F */
 static const struct unidata st61[] = {
-{0,dd4190,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4191,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4192,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4193,dd3745,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4194,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4195,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4196,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4197,dd3746,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4198,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4197,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4198,dd3746,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4199,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4200,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4201,dd3747,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4202,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4203,dd3748,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4201,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4202,dd3747,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4203,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4204,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4205,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+{0,dd4205,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4206,dd3748,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4207,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4208,dd3749,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4209,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4210,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 0430-043F */
 static const struct unidata st62[] = {
-{0,0,dd3749,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3750,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3751,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3752,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3753,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3754,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd353,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3755,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd353,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3756,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,dd3756,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3757,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0440-044F */
@@ -6103,18 +6218,18 @@ static const struct unidata st63[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,dd3757,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3758,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3759,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,dd3760,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3761,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0450-045F */
@@ -6125,7 +6240,7 @@ static const struct unidata st64[] = {
 {dd356,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,dd3761,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,dd3762,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd357,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -6138,82 +6253,69 @@ static const struct unidata st64[] = {
 };
 /* 0460-046F */
 static const struct unidata st65[] = {
-{0,dd4206,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4211,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4207,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4212,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4208,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4213,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4209,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4214,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4210,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4215,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4211,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4216,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4212,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4217,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4213,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4218,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0470-047F */
 static const struct unidata st66[] = {
-{0,dd4214,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4219,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4215,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4220,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4216,dd3762,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,dd3763,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd361,dd3763,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4221,dd3763,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,dd3764,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd361,dd3764,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd362,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4217,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4222,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4218,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4223,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4219,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4224,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4220,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4225,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0480-048F */
 static const struct unidata st67[] = {
-{0,dd4221,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4226,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Me,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Me,0,GBExtend,WBExtend,SBExtend},
-{0,dd4222,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4227,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4223,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4228,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4224,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4229,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0490-049F */
 static const struct unidata st68[] = {
-{0,dd4225,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4226,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4227,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4228,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4229,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4230,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4231,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4232,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
-};
-/* 04A0-04AF */
-static const struct unidata st69[] = {
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4233,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4234,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -6223,16 +6325,16 @@ static const struct unidata st69[] = {
 {0,dd4236,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4237,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
+};
+/* 04A0-04AF */
+static const struct unidata st69[] = {
 {0,dd4238,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4239,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4240,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
-};
-/* 04B0-04BF */
-static const struct unidata st70[] = {
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4241,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4242,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -6242,134 +6344,147 @@ static const struct unidata st70[] = {
 {0,dd4244,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4245,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
+};
+/* 04B0-04BF */
+static const struct unidata st70[] = {
 {0,dd4246,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4247,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4248,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4249,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4250,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4251,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4252,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4253,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 04C0-04CF */
 static const struct unidata st71[] = {
-{0,dd4249,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd363,dd4250,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4254,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd363,dd4255,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd364,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4251,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4256,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4252,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4257,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4253,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4258,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4254,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4259,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4255,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4260,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4256,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4261,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 04D0-04DF */
 static const struct unidata st72[] = {
-{dd365,dd4257,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd365,dd4262,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd366,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd367,dd4258,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd367,dd4263,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd368,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4259,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4264,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd369,dd4260,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd369,dd4265,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd370,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4261,dd3764,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,dd3765,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd371,dd3765,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4266,dd3765,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,dd3766,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd371,dd3766,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd372,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd373,dd4262,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd373,dd4267,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd374,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd375,dd3753,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd375,dd3754,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd376,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 04E0-04EF */
 static const struct unidata st73[] = {
-{0,dd4263,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4268,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd377,dd4264,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd377,dd4269,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd378,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd379,dd4265,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd379,dd4270,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd380,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd381,dd3756,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd381,dd3757,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd382,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4266,dd3766,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,dd3767,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd383,dd3767,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4271,dd3767,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,dd3768,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd383,dd3768,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd384,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd385,dd3760,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd385,dd3761,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd386,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd387,dd4267,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd387,dd4272,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd388,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 04F0-04FF */
 static const struct unidata st74[] = {
-{dd389,dd4268,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd389,dd4273,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd390,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd391,dd4269,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd391,dd4274,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd392,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd393,dd3758,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd393,dd3759,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd394,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4270,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4275,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd395,dd3759,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd395,dd3760,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd396,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4271,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4276,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4272,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4277,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4273,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4278,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0500-050F */
 static const struct unidata st75[] = {
-{0,dd4274,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4279,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4275,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4280,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4276,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4281,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4277,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4282,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4278,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4283,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4279,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4284,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4280,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4285,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4281,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4286,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0510-051F */
 static const struct unidata st76[] = {
-{0,dd4282,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4287,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4283,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4288,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{0,dd4289,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4290,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4291,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4292,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4293,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4294,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 0520-052F */
 static const struct unidata st77[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,dd4295,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4296,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -6386,24 +6501,8 @@ static const struct unidata st77[] = {
 /* 0530-053F */
 static const struct unidata st78[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,dd4284,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4285,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4286,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4287,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4288,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4289,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4290,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4291,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4292,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4293,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4294,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4295,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4296,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4297,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4298,0,0,Lu,0,GBOther,WBALetter,SBUpper}
-};
-/* 0540-054F */
-static const struct unidata st79[] = {
+{0,dd4298,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4299,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4300,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4301,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -6416,13 +6515,13 @@ static const struct unidata st79[] = {
 {0,dd4308,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4309,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4310,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4311,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4311,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+};
+/* 0540-054F */
+static const struct unidata st79[] = {
 {0,dd4312,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4313,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4314,0,0,Lu,0,GBOther,WBALetter,SBUpper}
-};
-/* 0550-055F */
-static const struct unidata st80[] = {
+{0,dd4314,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4315,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4316,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4317,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -6430,13 +6529,29 @@ static const struct unidata st80[] = {
 {0,dd4319,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4320,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4321,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4322,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4323,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4324,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4325,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4326,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4327,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+};
+/* 0550-055F */
+static const struct unidata st80[] = {
+{0,dd4328,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4329,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4330,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4331,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4332,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4333,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4334,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSContinue},
 {0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
@@ -6532,7 +6647,7 @@ static const struct unidata st85[] = {
 {0,0,0,20,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,21,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,22,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Pd,0,GBOther,WBOther,SBOther},
 {0,0,0,23,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 05C0-05CF */
@@ -6619,14 +6734,14 @@ static const struct unidata st90[] = {
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBMidNum,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBMidNum,SBSContinue},
+{0,0,0,0,Po,0,GBOther,WBMidNum,SBSContinue},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
@@ -6638,11 +6753,11 @@ static const struct unidata st91[] = {
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,30,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,31,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,32,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -6658,7 +6773,7 @@ static const struct unidata st92[] = {
 {dd400,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd401,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd402,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3768,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3769,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -6678,9 +6793,9 @@ static const struct unidata st93[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd2958,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd2959,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd2960,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd2961,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,27,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,28,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,29,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -6720,7 +6835,7 @@ static const struct unidata st95[] = {
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBNumeric,SBNumeric},
-{0,0,0,0,Po,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Po,0,GBOther,WBMidNum,SBNumeric},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
@@ -6747,7 +6862,7 @@ static const struct unidata st96[] = {
 /* 06C0-06CF */
 static const struct unidata st97[] = {
 {dd407,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3769,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3770,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd408,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -6767,10 +6882,10 @@ static const struct unidata st97[] = {
 static const struct unidata st98[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd2614,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd2615,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd409,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
-{0,0,dd2610,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd2611,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -6896,27 +7011,8 @@ static const struct unidata st104[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* 0760-076F */
-static const struct unidata st105[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
-};
 /* 07A0-07AF */
-static const struct unidata st106[] = {
+static const struct unidata st105[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -6935,7 +7031,7 @@ static const struct unidata st106[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 07B0-07BF */
-static const struct unidata st107[] = {
+static const struct unidata st106[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -6954,7 +7050,7 @@ static const struct unidata st107[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 07C0-07CF */
-static const struct unidata st108[] = {
+static const struct unidata st107[] = {
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
@@ -6973,7 +7069,7 @@ static const struct unidata st108[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 07E0-07EF */
-static const struct unidata st109[] = {
+static const struct unidata st108[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -6992,7 +7088,7 @@ static const struct unidata st109[] = {
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 07F0-07FF */
-static const struct unidata st110[] = {
+static const struct unidata st109[] = {
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -7001,7 +7097,7 @@ static const struct unidata st110[] = {
 {0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBMidNum,SBOther},
+{0,0,0,0,Po,0,GBOther,WBMidNum,SBSContinue},
 {0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
 {0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -7010,12 +7106,31 @@ static const struct unidata st110[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
+/* 0800-080F */
+static const struct unidata st110[] = {
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
 /* 0900-090F */
 static const struct unidata st111[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7039,7 +7154,7 @@ static const struct unidata st112[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3770,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3771,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd410,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7050,10 +7165,10 @@ static const struct unidata st112[] = {
 };
 /* 0930-093F */
 static const struct unidata st113[] = {
-{0,0,dd3771,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3772,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd411,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3772,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3773,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd412,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7064,12 +7179,12 @@ static const struct unidata st113[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,7,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter}
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
 /* 0940-094F */
 static const struct unidata st114[] = {
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -7078,10 +7193,10 @@ static const struct unidata st114[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
@@ -7127,8 +7242,8 @@ static const struct unidata st116[] = {
 /* 0970-097F */
 static const struct unidata st117[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -7147,8 +7262,8 @@ static const struct unidata st117[] = {
 static const struct unidata st118[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7217,23 +7332,23 @@ static const struct unidata st121[] = {
 {0,0,0,7,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Mc,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter}
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
 /* 09C0-09CF */
 static const struct unidata st122[] = {
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,dd3773,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3774,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd421,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd422,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{dd421,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd422,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
@@ -7300,7 +7415,7 @@ static const struct unidata st126[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7330,12 +7445,12 @@ static const struct unidata st127[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,7,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter}
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
 /* 0A40-0A4F */
 static const struct unidata st128[] = {
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -7355,7 +7470,7 @@ static const struct unidata st128[] = {
 /* 0A50-0A5F */
 static const struct unidata st129[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -7397,7 +7512,7 @@ static const struct unidata st131[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -7414,7 +7529,7 @@ static const struct unidata st132[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7463,12 +7578,12 @@ static const struct unidata st134[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,7,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter}
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
 /* 0AC0-0ACF */
 static const struct unidata st135[] = {
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -7477,10 +7592,10 @@ static const struct unidata st135[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
@@ -7544,19 +7659,19 @@ static const struct unidata st138[] = {
 };
 /* 0B40-0B4F */
 static const struct unidata st139[] = {
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,dd3774,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd432,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3775,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd432,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd433,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd434,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{dd433,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd434,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
@@ -7580,27 +7695,8 @@ static const struct unidata st140[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* 0B60-0B6F */
-static const struct unidata st141[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
-{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
-{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
-{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
-{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
-{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
-{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
-{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
-{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
-{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric}
-};
 /* 0B70-0B7F */
-static const struct unidata st142[] = {
+static const struct unidata st141[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -7619,7 +7715,7 @@ static const struct unidata st142[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0B80-0B8F */
-static const struct unidata st143[] = {
+static const struct unidata st142[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -7638,10 +7734,10 @@ static const struct unidata st143[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 0B90-0B9F */
-static const struct unidata st144[] = {
+static const struct unidata st143[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,dd3775,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3776,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd437,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7657,7 +7753,7 @@ static const struct unidata st144[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 0BA0-0BAF */
-static const struct unidata st145[] = {
+static const struct unidata st144[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -7676,7 +7772,7 @@ static const struct unidata st145[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 0BB0-0BBF */
-static const struct unidata st146[] = {
+static const struct unidata st145[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7692,30 +7788,30 @@ static const struct unidata st146[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Mc,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter}
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
 /* 0BC0-0BCF */
-static const struct unidata st147[] = {
+static const struct unidata st146[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,dd3776,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3777,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3777,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,dd3778,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd438,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd439,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd440,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{dd438,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd439,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd440,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0BD0-0BDF */
-static const struct unidata st148[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+static const struct unidata st147[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -7733,7 +7829,7 @@ static const struct unidata st148[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0BF0-0BFF */
-static const struct unidata st149[] = {
+static const struct unidata st148[] = {
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
@@ -7752,11 +7848,11 @@ static const struct unidata st149[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0C00-0C0F */
-static const struct unidata st150[] = {
+static const struct unidata st149[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7771,7 +7867,7 @@ static const struct unidata st150[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 0C10-0C1F */
-static const struct unidata st151[] = {
+static const struct unidata st150[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7790,7 +7886,7 @@ static const struct unidata st151[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 0C30-0C3F */
-static const struct unidata st152[] = {
+static const struct unidata st151[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7804,19 +7900,19 @@ static const struct unidata st152[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 0C40-0C4F */
-static const struct unidata st153[] = {
+static const struct unidata st152[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,dd3778,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,dd3779,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {dd441,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -7828,7 +7924,7 @@ static const struct unidata st153[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0C50-0C5F */
-static const struct unidata st154[] = {
+static const struct unidata st153[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -7837,21 +7933,40 @@ static const struct unidata st154[] = {
 {0,0,0,84,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,91,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
+/* 0C70-0C7F */
+static const struct unidata st154[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 0C80-0C8F */
 static const struct unidata st155[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7881,23 +7996,23 @@ static const struct unidata st156[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,7,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3779,0,Mn,0,GBExtend,WBExtend,SBExtend}
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,dd3780,0,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 0CC0-0CCF */
 static const struct unidata st157[] = {
-{dd442,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{dd442,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mc,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,dd3780,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{dd443,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd444,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3781,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{dd443,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd444,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd445,0,dd3781,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd446,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{dd445,0,dd3782,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd446,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -7941,31 +8056,88 @@ static const struct unidata st159[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 0D40-0D4F */
+/* 0D30-0D3F */
 static const struct unidata st160[] = {
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
+};
+/* 0D40-0D4F */
+static const struct unidata st161[] = {
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,dd3783,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,dd3784,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,dd3782,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3783,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd447,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd448,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd449,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{dd447,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd448,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd449,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
+/* 0D50-0D5F */
+static const struct unidata st162[] = {
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Mc,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
+/* 0D70-0D7F */
+static const struct unidata st163[] = {
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+};
 /* 0D80-0D8F */
-static const struct unidata st161[] = {
+static const struct unidata st164[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7980,7 +8152,7 @@ static const struct unidata st161[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 0D90-0D9F */
-static const struct unidata st162[] = {
+static const struct unidata st165[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -7999,7 +8171,7 @@ static const struct unidata st162[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 0DB0-0DBF */
-static const struct unidata st163[] = {
+static const struct unidata st166[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -8018,7 +8190,7 @@ static const struct unidata st163[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0DC0-0DCF */
-static const struct unidata st164[] = {
+static const struct unidata st167[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -8037,30 +8209,30 @@ static const struct unidata st164[] = {
 {0,0,0,0,Mc,0,GBExtend,WBExtend,SBExtend}
 };
 /* 0DD0-0DDF */
-static const struct unidata st165[] = {
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+static const struct unidata st168[] = {
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3784,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd450,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd451,0,dd3785,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd452,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd453,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,dd3785,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd450,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd451,0,dd3786,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd452,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd453,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mc,0,GBExtend,WBExtend,SBExtend}
 };
 /* 0DF0-0DFF */
-static const struct unidata st166[] = {
+static const struct unidata st169[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -8075,7 +8247,7 @@ static const struct unidata st166[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0E00-0E0F */
-static const struct unidata st167[] = {
+static const struct unidata st170[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
@@ -8094,7 +8266,7 @@ static const struct unidata st167[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
 /* 0E10-0E1F */
-static const struct unidata st168[] = {
+static const struct unidata st171[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
@@ -8113,11 +8285,11 @@ static const struct unidata st168[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
 /* 0E30-0E3F */
-static const struct unidata st169[] = {
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+static const struct unidata st172[] = {
+{0,0,0,0,Lo,0,GBExtend,WBExtend,SBOLetter},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd454,0,0,0,Lo,CD,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBExtend,WBExtend,SBOLetter},
+{dd454,0,0,0,Lo,CD,GBExtend,WBExtend,SBOLetter},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -8132,13 +8304,13 @@ static const struct unidata st169[] = {
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther}
 };
 /* 0E40-0E4F */
-static const struct unidata st170[] = {
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+static const struct unidata st173[] = {
+{0,0,0,0,Lo,0,GBPrepend,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBPrepend,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBPrepend,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBPrepend,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBPrepend,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBExtend,WBExtend,SBOLetter},
 {0,0,0,0,Lm,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,107,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -8151,7 +8323,7 @@ static const struct unidata st170[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
 /* 0E50-0E5F */
-static const struct unidata st171[] = {
+static const struct unidata st174[] = {
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
@@ -8170,7 +8342,7 @@ static const struct unidata st171[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0E80-0E8F */
-static const struct unidata st172[] = {
+static const struct unidata st175[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
@@ -8189,7 +8361,7 @@ static const struct unidata st172[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0E90-0E9F */
-static const struct unidata st173[] = {
+static const struct unidata st176[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -8208,7 +8380,7 @@ static const struct unidata st173[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
 /* 0EA0-0EAF */
-static const struct unidata st174[] = {
+static const struct unidata st177[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
@@ -8227,11 +8399,11 @@ static const struct unidata st174[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
 /* 0EB0-0EBF */
-static const struct unidata st175[] = {
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+static const struct unidata st178[] = {
+{0,0,0,0,Lo,0,GBExtend,WBExtend,SBOLetter},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd455,0,0,0,Lo,CD,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBExtend,WBExtend,SBOLetter},
+{dd455,0,0,0,Lo,CD,GBExtend,WBExtend,SBOLetter},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -8246,12 +8418,12 @@ static const struct unidata st175[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0EC0-0ECF */
-static const struct unidata st176[] = {
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+static const struct unidata st179[] = {
+{0,0,0,0,Lo,0,GBPrepend,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBPrepend,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBPrepend,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBPrepend,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBPrepend,WBOther,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lm,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -8265,7 +8437,7 @@ static const struct unidata st176[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0ED0-0EDF */
-static const struct unidata st177[] = {
+static const struct unidata st180[] = {
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
@@ -8284,7 +8456,7 @@ static const struct unidata st177[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0F00-0F0F */
-static const struct unidata st178[] = {
+static const struct unidata st181[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -8303,7 +8475,7 @@ static const struct unidata st178[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
 /* 0F10-0F1F */
-static const struct unidata st179[] = {
+static const struct unidata st182[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
@@ -8322,7 +8494,7 @@ static const struct unidata st179[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 0F20-0F2F */
-static const struct unidata st180[] = {
+static const struct unidata st183[] = {
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
@@ -8341,7 +8513,7 @@ static const struct unidata st180[] = {
 {0,0,0,0,No,0,GBOther,WBOther,SBOther}
 };
 /* 0F30-0F3F */
-static const struct unidata st181[] = {
+static const struct unidata st184[] = {
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
@@ -8356,11 +8528,11 @@ static const struct unidata st181[] = {
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOther}
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
 /* 0F40-0F4F */
-static const struct unidata st182[] = {
+static const struct unidata st185[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -8379,7 +8551,7 @@ static const struct unidata st182[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 0F50-0F5F */
-static const struct unidata st183[] = {
+static const struct unidata st186[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd461,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -8398,7 +8570,7 @@ static const struct unidata st183[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 0F60-0F6F */
-static const struct unidata st184[] = {
+static const struct unidata st187[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -8410,14 +8582,14 @@ static const struct unidata st184[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd464,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0F70-0F7F */
-static const struct unidata st185[] = {
+static const struct unidata st188[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,129,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,130,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -8433,10 +8605,10 @@ static const struct unidata st185[] = {
 {0,0,0,130,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,130,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter}
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
 /* 0F80-0F8F */
-static const struct unidata st186[] = {
+static const struct unidata st189[] = {
 {0,0,0,130,Mn,0,GBExtend,WBExtend,SBExtend},
 {dd471,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -8455,7 +8627,7 @@ static const struct unidata st186[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 0F90-0F9F */
-static const struct unidata st187[] = {
+static const struct unidata st190[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -8474,7 +8646,7 @@ static const struct unidata st187[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 0FA0-0FAF */
-static const struct unidata st188[] = {
+static const struct unidata st191[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {dd474,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -8493,7 +8665,7 @@ static const struct unidata st188[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 0FB0-0FBF */
-static const struct unidata st189[] = {
+static const struct unidata st192[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -8512,7 +8684,7 @@ static const struct unidata st189[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 0FC0-0FCF */
-static const struct unidata st190[] = {
+static const struct unidata st193[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -8527,16 +8699,16 @@ static const struct unidata st190[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 0FD0-0FDF */
-static const struct unidata st191[] = {
+static const struct unidata st194[] = {
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -8550,45 +8722,45 @@ static const struct unidata st191[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1020-102F */
-static const struct unidata st192[] = {
+static const struct unidata st195[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3786,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,dd3787,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd478,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 1030-103F */
-static const struct unidata st193[] = {
+static const struct unidata st196[] = {
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,7,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
 /* 1040-104F */
-static const struct unidata st194[] = {
+static const struct unidata st197[] = {
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
@@ -8607,45 +8779,105 @@ static const struct unidata st194[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
 /* 1050-105F */
-static const struct unidata st195[] = {
+static const struct unidata st198[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend}
+};
+/* 1060-106F */
+static const struct unidata st199[] = {
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 1070-107F */
+static const struct unidata st200[] = {
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 1080-108F */
+static const struct unidata st201[] = {
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
+};
+/* 1090-109F */
+static const struct unidata st202[] = {
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 10A0-10AF */
-static const struct unidata st196[] = {
-{0,dd4322,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4323,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4324,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4325,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4326,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4327,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4328,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4329,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4330,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4331,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4332,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4333,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4334,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st203[] = {
 {0,dd4335,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4336,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4337,0,0,Lu,0,GBOther,WBALetter,SBUpper}
-};
-/* 10B0-10BF */
-static const struct unidata st197[] = {
+{0,dd4337,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4338,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4339,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4340,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -8658,19 +8890,35 @@ static const struct unidata st197[] = {
 {0,dd4347,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4348,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4349,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4350,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4350,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+};
+/* 10B0-10BF */
+static const struct unidata st204[] = {
 {0,dd4351,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4352,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4353,0,0,Lu,0,GBOther,WBALetter,SBUpper}
-};
-/* 10C0-10CF */
-static const struct unidata st198[] = {
+{0,dd4353,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4354,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4355,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4356,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4357,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4358,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4359,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4360,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4361,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4362,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4363,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4364,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4365,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4366,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+};
+/* 10C0-10CF */
+static const struct unidata st205[] = {
+{0,dd4367,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4368,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4369,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4370,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4371,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4372,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -8683,7 +8931,7 @@ static const struct unidata st198[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 10F0-10FF */
-static const struct unidata st199[] = {
+static const struct unidata st206[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -8702,7 +8950,7 @@ static const struct unidata st199[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1100-110F */
-static const struct unidata st200[] = {
+static const struct unidata st207[] = {
 {0,0,0,0,Lo,0,GBL,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBL,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBL,WBALetter,SBOLetter},
@@ -8721,7 +8969,7 @@ static const struct unidata st200[] = {
 {0,0,0,0,Lo,0,GBL,WBALetter,SBOLetter}
 };
 /* 1150-115F */
-static const struct unidata st201[] = {
+static const struct unidata st208[] = {
 {0,0,0,0,Lo,0,GBL,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBL,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBL,WBALetter,SBOLetter},
@@ -8740,7 +8988,7 @@ static const struct unidata st201[] = {
 {0,0,0,0,Lo,0,GBL,WBALetter,SBOLetter}
 };
 /* 1160-116F */
-static const struct unidata st202[] = {
+static const struct unidata st209[] = {
 {0,0,0,0,Lo,0,GBV,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBV,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBV,WBALetter,SBOLetter},
@@ -8759,7 +9007,7 @@ static const struct unidata st202[] = {
 {0,0,0,0,Lo,0,GBV,WBALetter,SBOLetter}
 };
 /* 11A0-11AF */
-static const struct unidata st203[] = {
+static const struct unidata st210[] = {
 {0,0,0,0,Lo,0,GBV,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBV,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBV,WBALetter,SBOLetter},
@@ -8778,7 +9026,7 @@ static const struct unidata st203[] = {
 {0,0,0,0,Lo,0,GBT,WBALetter,SBOLetter}
 };
 /* 11B0-11BF */
-static const struct unidata st204[] = {
+static const struct unidata st211[] = {
 {0,0,0,0,Lo,0,GBT,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBT,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBT,WBALetter,SBOLetter},
@@ -8797,7 +9045,7 @@ static const struct unidata st204[] = {
 {0,0,0,0,Lo,0,GBT,WBALetter,SBOLetter}
 };
 /* 11F0-11FF */
-static const struct unidata st205[] = {
+static const struct unidata st212[] = {
 {0,0,0,0,Lo,0,GBT,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBT,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBT,WBALetter,SBOLetter},
@@ -8816,7 +9064,7 @@ static const struct unidata st205[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1240-124F */
-static const struct unidata st206[] = {
+static const struct unidata st213[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -8835,7 +9083,7 @@ static const struct unidata st206[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1250-125F */
-static const struct unidata st207[] = {
+static const struct unidata st214[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -8854,7 +9102,7 @@ static const struct unidata st207[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 12B0-12BF */
-static const struct unidata st208[] = {
+static const struct unidata st215[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -8873,7 +9121,7 @@ static const struct unidata st208[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 12C0-12CF */
-static const struct unidata st209[] = {
+static const struct unidata st216[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -8892,7 +9140,7 @@ static const struct unidata st209[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 12D0-12DF */
-static const struct unidata st210[] = {
+static const struct unidata st217[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -8911,7 +9159,7 @@ static const struct unidata st210[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 1350-135F */
-static const struct unidata st211[] = {
+static const struct unidata st218[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -8930,7 +9178,7 @@ static const struct unidata st211[] = {
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 1360-136F */
-static const struct unidata st212[] = {
+static const struct unidata st219[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
@@ -8949,7 +9197,7 @@ static const struct unidata st212[] = {
 {0,0,0,0,No,0,GBOther,WBOther,SBOther}
 };
 /* 1370-137F */
-static const struct unidata st213[] = {
+static const struct unidata st220[] = {
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
@@ -8968,7 +9216,7 @@ static const struct unidata st213[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1390-139F */
-static const struct unidata st214[] = {
+static const struct unidata st221[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -8987,7 +9235,7 @@ static const struct unidata st214[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 13F0-13FF */
-static const struct unidata st215[] = {
+static const struct unidata st222[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9006,7 +9254,7 @@ static const struct unidata st215[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1400-140F */
-static const struct unidata st216[] = {
+static const struct unidata st223[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9025,7 +9273,7 @@ static const struct unidata st216[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 1660-166F */
-static const struct unidata st217[] = {
+static const struct unidata st224[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9044,7 +9292,7 @@ static const struct unidata st217[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 1670-167F */
-static const struct unidata st218[] = {
+static const struct unidata st225[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9063,7 +9311,7 @@ static const struct unidata st218[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1680-168F */
-static const struct unidata st219[] = {
+static const struct unidata st226[] = {
 {0,0,0,0,Zs,0,GBOther,WBOther,SBSp},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9082,7 +9330,7 @@ static const struct unidata st219[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 1690-169F */
-static const struct unidata st220[] = {
+static const struct unidata st227[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9101,7 +9349,7 @@ static const struct unidata st220[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 16E0-16EF */
-static const struct unidata st221[] = {
+static const struct unidata st228[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9120,7 +9368,7 @@ static const struct unidata st221[] = {
 {0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter}
 };
 /* 16F0-16FF */
-static const struct unidata st222[] = {
+static const struct unidata st229[] = {
 {0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -9139,7 +9387,7 @@ static const struct unidata st222[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1700-170F */
-static const struct unidata st223[] = {
+static const struct unidata st230[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9158,7 +9406,7 @@ static const struct unidata st223[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 1710-171F */
-static const struct unidata st224[] = {
+static const struct unidata st231[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -9177,7 +9425,7 @@ static const struct unidata st224[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1730-173F */
-static const struct unidata st225[] = {
+static const struct unidata st232[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -9196,7 +9444,7 @@ static const struct unidata st225[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1750-175F */
-static const struct unidata st226[] = {
+static const struct unidata st233[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -9215,7 +9463,7 @@ static const struct unidata st226[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1770-177F */
-static const struct unidata st227[] = {
+static const struct unidata st234[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -9234,14 +9482,14 @@ static const struct unidata st227[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 17B0-17BF */
-static const struct unidata st228[] = {
+static const struct unidata st235[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -9249,20 +9497,20 @@ static const struct unidata st228[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter}
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
 /* 17C0-17CF */
-static const struct unidata st229[] = {
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
+static const struct unidata st236[] = {
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -9272,7 +9520,7 @@ static const struct unidata st229[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 17D0-17DF */
-static const struct unidata st230[] = {
+static const struct unidata st237[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -9291,7 +9539,7 @@ static const struct unidata st230[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 17E0-17EF */
-static const struct unidata st231[] = {
+static const struct unidata st238[] = {
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
@@ -9310,7 +9558,7 @@ static const struct unidata st231[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 17F0-17FF */
-static const struct unidata st232[] = {
+static const struct unidata st239[] = {
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
@@ -9329,16 +9577,16 @@ static const struct unidata st232[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1800-180F */
-static const struct unidata st233[] = {
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+static const struct unidata st240[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSContinue},
 {0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Pd,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSContinue},
 {0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -9348,7 +9596,7 @@ static const struct unidata st233[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1840-184F */
-static const struct unidata st234[] = {
+static const struct unidata st241[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9367,7 +9615,7 @@ static const struct unidata st234[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 1870-187F */
-static const struct unidata st235[] = {
+static const struct unidata st242[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9386,7 +9634,7 @@ static const struct unidata st235[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 18A0-18AF */
-static const struct unidata st236[] = {
+static const struct unidata st243[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9397,7 +9645,7 @@ static const struct unidata st236[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,228,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -9405,7 +9653,7 @@ static const struct unidata st236[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1910-191F */
-static const struct unidata st237[] = {
+static const struct unidata st244[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9424,35 +9672,35 @@ static const struct unidata st237[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1920-192F */
-static const struct unidata st238[] = {
+static const struct unidata st245[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1930-193F */
-static const struct unidata st239[] = {
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+static const struct unidata st246[] = {
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,222,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -9462,7 +9710,7 @@ static const struct unidata st239[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1940-194F */
-static const struct unidata st240[] = {
+static const struct unidata st247[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -9481,7 +9729,7 @@ static const struct unidata st240[] = {
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric}
 };
 /* 1960-196F */
-static const struct unidata st241[] = {
+static const struct unidata st248[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
@@ -9500,7 +9748,7 @@ static const struct unidata st241[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1970-197F */
-static const struct unidata st242[] = {
+static const struct unidata st249[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
@@ -9519,7 +9767,7 @@ static const struct unidata st242[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 19A0-19AF */
-static const struct unidata st243[] = {
+static const struct unidata st250[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
@@ -9538,27 +9786,27 @@ static const struct unidata st243[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 19B0-19BF */
-static const struct unidata st244[] = {
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter}
+static const struct unidata st251[] = {
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
 /* 19C0-19CF */
-static const struct unidata st245[] = {
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
+static const struct unidata st252[] = {
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
@@ -9566,8 +9814,8 @@ static const struct unidata st245[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -9576,7 +9824,7 @@ static const struct unidata st245[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 19D0-19DF */
-static const struct unidata st246[] = {
+static const struct unidata st253[] = {
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
@@ -9595,7 +9843,7 @@ static const struct unidata st246[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
 /* 19E0-19EF */
-static const struct unidata st247[] = {
+static const struct unidata st254[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -9614,7 +9862,7 @@ static const struct unidata st247[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 1A10-1A1F */
-static const struct unidata st248[] = {
+static const struct unidata st255[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9624,37 +9872,37 @@ static const struct unidata st248[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
 /* 1B00-1B0F */
-static const struct unidata st249[] = {
+static const struct unidata st256[] = {
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3787,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd480,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,dd3788,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd481,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd480,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,dd3789,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd482,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd481,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,dd3790,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd483,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd482,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,dd3791,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd483,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3792,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd484,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 1B10-1B1F */
-static const struct unidata st250[] = {
+static const struct unidata st257[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3792,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,dd3793,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {dd485,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9671,31 +9919,31 @@ static const struct unidata st250[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
 /* 1B30-1B3F */
-static const struct unidata st251[] = {
+static const struct unidata st258[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,7,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,dd3793,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{dd486,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
 {0,0,dd3794,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{dd487,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3795,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3796,0,Mc,0,GBOther,WBALetter,SBOLetter}
+{dd486,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,dd3795,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{dd487,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,dd3796,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,dd3797,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
 /* 1B40-1B4F */
-static const struct unidata st252[] = {
-{dd488,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{dd489,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,dd3797,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{dd490,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,9,Mc,0,GBOther,WBOther,SBOther},
+static const struct unidata st259[] = {
+{dd488,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{dd489,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,dd3798,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{dd490,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,9,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -9709,7 +9957,7 @@ static const struct unidata st252[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1B50-1B5F */
-static const struct unidata st253[] = {
+static const struct unidata st260[] = {
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
@@ -9728,7 +9976,7 @@ static const struct unidata st253[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBSTerm}
 };
 /* 1B60-1B6F */
-static const struct unidata st254[] = {
+static const struct unidata st261[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -9747,7 +9995,7 @@ static const struct unidata st254[] = {
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 1B70-1B7F */
-static const struct unidata st255[] = {
+static const struct unidata st262[] = {
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -9765,8 +10013,122 @@ static const struct unidata st255[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
+/* 1B80-1B8F */
+static const struct unidata st263[] = {
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+};
+/* 1BA0-1BAF */
+static const struct unidata st264[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,9,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+};
+/* 1C20-1C2F */
+static const struct unidata st265[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend}
+};
+/* 1C30-1C3F */
+static const struct unidata st266[] = {
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,7,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther}
+};
+/* 1C40-1C4F */
+static const struct unidata st267[] = {
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+};
+/* 1C70-1C7F */
+static const struct unidata st268[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm}
+};
 /* 1D20-1D2F */
-static const struct unidata st256[] = {
+static const struct unidata st269[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -9785,7 +10147,7 @@ static const struct unidata st256[] = {
 {0,0,0,0,Lm,0,GBOther,WBALetter,SBLower}
 };
 /* 1D30-1D3F */
-static const struct unidata st257[] = {
+static const struct unidata st270[] = {
 {dd494,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd495,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd496,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
@@ -9804,7 +10166,7 @@ static const struct unidata st257[] = {
 {dd508,0,0,0,Lm,CD,GBOther,WBALetter,SBLower}
 };
 /* 1D40-1D4F */
-static const struct unidata st258[] = {
+static const struct unidata st271[] = {
 {dd509,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd510,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd511,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
@@ -9823,7 +10185,7 @@ static const struct unidata st258[] = {
 {dd522,0,0,0,Lm,CD,GBOther,WBALetter,SBLower}
 };
 /* 1D50-1D5F */
-static const struct unidata st259[] = {
+static const struct unidata st272[] = {
 {dd523,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd524,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd10,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
@@ -9842,7 +10204,7 @@ static const struct unidata st259[] = {
 {dd536,0,0,0,Lm,CD,GBOther,WBALetter,SBLower}
 };
 /* 1D60-1D6F */
-static const struct unidata st260[] = {
+static const struct unidata st273[] = {
 {dd337,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd537,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
@@ -9861,7 +10223,7 @@ static const struct unidata st260[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1D70-1D7F */
-static const struct unidata st261[] = {
+static const struct unidata st274[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -9880,7 +10242,7 @@ static const struct unidata st261[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1D90-1D9F */
-static const struct unidata st262[] = {
+static const struct unidata st275[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -9899,7 +10261,7 @@ static const struct unidata st262[] = {
 {dd520,0,0,0,Lm,CD,GBOther,WBALetter,SBLower}
 };
 /* 1DA0-1DAF */
-static const struct unidata st263[] = {
+static const struct unidata st276[] = {
 {dd544,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd545,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd546,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
@@ -9918,7 +10280,7 @@ static const struct unidata st263[] = {
 {dd559,0,0,0,Lm,CD,GBOther,WBALetter,SBLower}
 };
 /* 1DB0-1DBF */
-static const struct unidata st264[] = {
+static const struct unidata st277[] = {
 {dd560,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd561,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd562,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
@@ -9937,7 +10299,7 @@ static const struct unidata st264[] = {
 {dd333,0,0,0,Lm,CD,GBOther,WBALetter,SBLower}
 };
 /* 1DC0-1DCF */
-static const struct unidata st265[] = {
+static const struct unidata st278[] = {
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -9949,6 +10311,44 @@ static const struct unidata st265[] = {
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,234,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,214,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend}
+};
+/* 1DD0-1DDF */
+static const struct unidata st279[] = {
+{0,0,0,202,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend}
+};
+/* 1DE0-1DEF */
+static const struct unidata st280[] = {
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -9956,7 +10356,7 @@ static const struct unidata st265[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1DF0-1DFF */
-static const struct unidata st266[] = {
+static const struct unidata st281[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -9975,449 +10375,449 @@ static const struct unidata st266[] = {
 {0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 1E00-1E0F */
-static const struct unidata st267[] = {
-{dd575,dd4360,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st282[] = {
+{dd575,dd4373,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd576,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd577,dd4361,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd577,dd4374,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd578,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd579,dd4362,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd579,dd4375,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd580,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd581,dd4363,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd581,dd4376,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd582,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd583,dd3677,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd583,dd3678,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd584,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd585,dd4364,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd585,dd4377,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd586,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd587,dd4365,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd587,dd4378,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd588,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd589,dd4366,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd589,dd4379,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd590,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1E10-1E1F */
-static const struct unidata st268[] = {
-{dd591,dd4367,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st283[] = {
+{dd591,dd4380,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd592,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd593,dd4368,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd593,dd4381,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd594,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd595,dd4369,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd595,dd4382,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd596,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd597,dd4370,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd597,dd4383,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd598,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd599,dd4371,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd599,dd4384,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd600,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd601,dd4372,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd601,dd4385,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd602,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd603,dd3710,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd603,dd3711,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd604,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd605,dd3640,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd605,dd3641,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd606,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1E20-1E2F */
-static const struct unidata st269[] = {
-{dd607,dd4373,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st284[] = {
+{dd607,dd4386,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd608,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd609,dd4374,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd609,dd4387,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd610,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd611,dd4375,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd611,dd4388,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd612,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd613,dd4376,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd613,dd4389,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd614,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd615,dd4377,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd615,dd4390,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd616,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd617,dd4378,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd617,dd4391,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd618,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd619,dd4379,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd619,dd4392,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd620,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd621,dd3679,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd621,dd3680,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd622,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1E30-1E3F */
-static const struct unidata st270[] = {
-{dd623,dd4380,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st285[] = {
+{dd623,dd4393,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd624,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd625,dd4381,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd625,dd4394,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd626,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd627,dd4382,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd627,dd4395,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd628,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd629,dd4383,dd3798,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd630,0,dd3799,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd631,dd3799,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd629,dd4396,dd3799,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd630,0,dd3800,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd631,dd3800,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd632,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd633,dd4384,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd633,dd4397,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd634,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd635,dd4385,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd635,dd4398,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd636,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd637,dd4386,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd637,dd4399,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd638,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1E40-1E4F */
-static const struct unidata st271[] = {
-{dd639,dd4387,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st286[] = {
+{dd639,dd4400,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd640,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd641,dd4388,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd641,dd4401,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd642,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd643,dd4389,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd643,dd4402,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd644,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd645,dd4390,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd645,dd4403,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd646,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd647,dd4391,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd647,dd4404,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd648,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd649,dd4392,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd649,dd4405,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd650,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd651,dd4393,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd651,dd4406,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd652,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd653,dd4394,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd653,dd4407,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd654,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1E50-1E5F */
-static const struct unidata st272[] = {
-{dd655,dd4395,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st287[] = {
+{dd655,dd4408,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd656,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd657,dd4396,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd657,dd4409,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd658,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd659,dd4397,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd659,dd4410,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd660,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd661,dd4398,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd661,dd4411,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd662,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd663,dd4399,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd663,dd4412,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd664,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd665,dd4400,dd3800,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd666,0,dd3801,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd667,dd3801,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd665,dd4413,dd3801,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd666,0,dd3802,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd667,dd3802,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd668,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd669,dd4401,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd669,dd4414,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd670,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1E60-1E6F */
-static const struct unidata st273[] = {
-{dd671,dd4402,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st288[] = {
+{dd671,dd4415,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd672,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd673,dd4403,dd3802,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd674,0,dd3803,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd675,dd3692,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd673,dd4416,dd3803,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd674,0,dd3804,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd675,dd3693,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd676,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd677,dd3694,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd677,dd3695,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd678,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd679,dd3803,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd679,dd3804,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd680,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd681,dd4404,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd681,dd4417,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd682,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd683,dd4405,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd683,dd4418,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd684,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd685,dd4406,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd685,dd4419,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd686,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1E70-1E7F */
-static const struct unidata st274[] = {
-{dd687,dd4407,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st289[] = {
+{dd687,dd4420,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd688,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd689,dd4408,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd689,dd4421,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd690,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd691,dd4409,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd691,dd4422,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd692,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd693,dd4410,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd693,dd4423,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd694,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd695,dd3696,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd695,dd3697,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd696,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd697,dd3698,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd697,dd3699,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd698,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd699,dd4411,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd699,dd4424,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd700,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd701,dd4412,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd701,dd4425,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd702,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1E80-1E8F */
-static const struct unidata st275[] = {
-{dd703,dd4413,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st290[] = {
+{dd703,dd4426,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd704,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd705,dd4414,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd705,dd4427,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd706,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd707,dd4415,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd707,dd4428,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd708,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd709,dd4416,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd709,dd4429,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd710,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd711,dd4417,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd711,dd4430,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd712,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd713,dd4418,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd713,dd4431,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd714,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd715,dd4419,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd715,dd4432,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd716,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd717,dd4420,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd717,dd4433,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd718,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1E90-1E9F */
-static const struct unidata st276[] = {
-{dd719,dd4421,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st291[] = {
+{dd719,dd4434,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd720,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd721,dd4422,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd721,dd4435,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd722,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd723,dd4423,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd723,dd4436,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd724,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd725,dd725,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
 {dd726,dd726,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
 {dd727,dd727,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
 {dd728,dd728,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
 {dd729,dd729,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd730,dd4402,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd730,dd4415,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4000,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1EA0-1EAF */
-static const struct unidata st277[] = {
-{dd731,dd4424,dd3804,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd732,0,dd3805,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd733,dd4425,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st292[] = {
+{dd731,dd4437,dd3805,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd732,0,dd3806,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd733,dd4438,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd734,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd735,dd4426,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd735,dd4439,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd736,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd737,dd4427,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd737,dd4440,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd738,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd739,dd4428,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd739,dd4441,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd740,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd741,dd4429,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd741,dd4442,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd742,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd743,dd4430,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd743,dd4443,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd744,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd745,dd4431,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd745,dd4444,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd746,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1EB0-1EBF */
-static const struct unidata st278[] = {
-{dd747,dd4432,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st293[] = {
+{dd747,dd4445,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd748,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd749,dd4433,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd749,dd4446,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd750,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd751,dd4434,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd751,dd4447,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd752,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd753,dd4435,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd753,dd4448,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd754,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd755,dd4436,dd3806,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd756,0,dd3807,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd757,dd4437,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd755,dd4449,dd3807,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd756,0,dd3808,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd757,dd4450,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd758,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd759,dd4438,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd759,dd4451,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd760,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd761,dd4439,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd761,dd4452,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd762,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1EC0-1ECF */
-static const struct unidata st279[] = {
-{dd763,dd4440,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st294[] = {
+{dd763,dd4453,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd764,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd765,dd4441,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd765,dd4454,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd766,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd767,dd4442,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd767,dd4455,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd768,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd769,dd3807,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd769,dd3808,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd770,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd771,dd4443,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd771,dd4456,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd772,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd773,dd4444,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd773,dd4457,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd774,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd775,dd4445,dd3808,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd776,0,dd3809,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd777,dd4446,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd775,dd4458,dd3809,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd776,0,dd3810,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd777,dd4459,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd778,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1ED0-1EDF */
-static const struct unidata st280[] = {
-{dd779,dd4447,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st295[] = {
+{dd779,dd4460,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd780,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd781,dd4448,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd781,dd4461,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd782,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd783,dd4449,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd783,dd4462,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd784,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd785,dd4450,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd785,dd4463,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd786,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd787,dd3809,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd787,dd3810,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd788,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd789,dd4451,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd789,dd4464,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd790,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd791,dd4452,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd791,dd4465,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd792,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd793,dd4453,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd793,dd4466,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd794,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1EE0-1EEF */
-static const struct unidata st281[] = {
-{dd795,dd4454,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st296[] = {
+{dd795,dd4467,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd796,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd797,dd4455,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd797,dd4468,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd798,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd799,dd4456,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd799,dd4469,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd800,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd801,dd4457,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd801,dd4470,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd802,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd803,dd4458,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd803,dd4471,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd804,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd805,dd4459,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd805,dd4472,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd806,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd807,dd4460,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd807,dd4473,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd808,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd809,dd4461,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd809,dd4474,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd810,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1EF0-1EFF */
-static const struct unidata st282[] = {
-{dd811,dd4462,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st297[] = {
+{dd811,dd4475,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd812,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd813,dd4463,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd813,dd4476,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd814,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd815,dd4464,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd815,dd4477,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd816,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd817,dd4465,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd817,dd4478,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd818,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd819,dd4466,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd819,dd4479,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd820,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{0,dd4480,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4481,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4482,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 1F00-1F0F */
-static const struct unidata st283[] = {
-{dd821,0,dd3810,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd822,0,dd3811,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd823,0,dd3812,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd824,0,dd3813,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd825,0,dd3814,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd826,0,dd3815,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd827,0,dd3816,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd828,0,dd3817,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd829,dd4467,dd3818,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd830,dd4468,dd3819,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd831,dd4469,dd3820,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd832,dd4470,dd3821,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd833,dd4471,dd3822,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd834,dd4472,dd3823,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd835,dd4473,dd3824,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd836,dd4474,dd3825,0,Lu,0,GBOther,WBALetter,SBUpper}
+static const struct unidata st298[] = {
+{dd821,0,dd3811,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd822,0,dd3812,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd823,0,dd3813,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd824,0,dd3814,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd825,0,dd3815,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd826,0,dd3816,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd827,0,dd3817,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd828,0,dd3818,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd829,dd4483,dd3819,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd830,dd4484,dd3820,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd831,dd4485,dd3821,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd832,dd4486,dd3822,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd833,dd4487,dd3823,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd834,dd4488,dd3824,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd835,dd4489,dd3825,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd836,dd4490,dd3826,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 1F10-1F1F */
-static const struct unidata st284[] = {
-{dd837,0,dd3826,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd838,0,dd3827,0,Ll,0,GBOther,WBALetter,SBLower},
+static const struct unidata st299[] = {
+{dd837,0,dd3827,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd838,0,dd3828,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd839,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd840,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd841,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd842,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd843,dd4475,dd3828,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd844,dd4476,dd3829,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd845,dd4477,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd846,dd4478,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd847,dd4479,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd848,dd4480,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd843,dd4491,dd3829,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd844,dd4492,dd3830,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd845,dd4493,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd846,dd4494,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd847,dd4495,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd848,dd4496,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1F20-1F2F */
-static const struct unidata st285[] = {
-{dd849,0,dd3830,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd850,0,dd3831,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd851,0,dd3832,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd852,0,dd3833,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd853,0,dd3834,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd854,0,dd3835,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd855,0,dd3836,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd856,0,dd3837,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd857,dd4481,dd3838,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd858,dd4482,dd3839,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd859,dd4483,dd3840,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd860,dd4484,dd3841,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd861,dd4485,dd3842,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd862,dd4486,dd3843,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd863,dd4487,dd3844,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd864,dd4488,dd3845,0,Lu,0,GBOther,WBALetter,SBUpper}
+static const struct unidata st300[] = {
+{dd849,0,dd3831,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd850,0,dd3832,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd851,0,dd3833,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd852,0,dd3834,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd853,0,dd3835,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd854,0,dd3836,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd855,0,dd3837,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd856,0,dd3838,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd857,dd4497,dd3839,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd858,dd4498,dd3840,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd859,dd4499,dd3841,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd860,dd4500,dd3842,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd861,dd4501,dd3843,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd862,dd4502,dd3844,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd863,dd4503,dd3845,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd864,dd4504,dd3846,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 1F30-1F3F */
-static const struct unidata st286[] = {
-{dd865,0,dd3846,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd866,0,dd3847,0,Ll,0,GBOther,WBALetter,SBLower},
+static const struct unidata st301[] = {
+{dd865,0,dd3847,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd866,0,dd3848,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd867,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd868,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd869,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd870,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd871,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd872,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd873,dd4489,dd3848,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd874,dd4490,dd3849,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd875,dd4491,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd876,dd4492,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd877,dd4493,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd878,dd4494,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd879,dd4495,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd880,dd4496,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+{dd873,dd4505,dd3849,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd874,dd4506,dd3850,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd875,dd4507,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd876,dd4508,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd877,dd4509,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd878,dd4510,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd879,dd4511,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd880,dd4512,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 1F40-1F4F */
-static const struct unidata st287[] = {
-{dd881,0,dd3850,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd882,0,dd3851,0,Ll,0,GBOther,WBALetter,SBLower},
+static const struct unidata st302[] = {
+{dd881,0,dd3851,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd882,0,dd3852,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd883,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd884,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd885,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd886,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd887,dd4497,dd3852,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd888,dd4498,dd3853,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd889,dd4499,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd890,dd4500,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd891,dd4501,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd892,dd4502,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd887,dd4513,dd3853,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd888,dd4514,dd3854,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd889,dd4515,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd890,dd4516,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd891,dd4517,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd892,dd4518,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1F50-1F5F */
-static const struct unidata st288[] = {
-{dd893,dd893,dd3854,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd894,0,dd3855,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd895,dd4503,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+static const struct unidata st303[] = {
+{dd893,dd893,dd3855,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd894,0,dd3856,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd895,dd4519,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
 {dd896,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd897,dd4504,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd897,dd4520,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
 {dd898,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd899,dd4505,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd899,dd4521,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
 {dd900,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd901,dd4506,dd3856,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd901,dd4522,dd3857,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd902,dd4507,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd902,dd4523,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd903,dd4508,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd903,dd4524,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd904,dd4509,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+{dd904,dd4525,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 1F60-1F6F */
-static const struct unidata st289[] = {
-{dd905,0,dd3857,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd906,0,dd3858,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd907,0,dd3859,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd908,0,dd3860,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd909,0,dd3861,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd910,0,dd3862,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd911,0,dd3863,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd912,0,dd3864,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd913,dd4510,dd3865,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd914,dd4511,dd3866,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd915,dd4512,dd3867,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd916,dd4513,dd3868,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd917,dd4514,dd3869,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd918,dd4515,dd3870,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd919,dd4516,dd3871,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd920,dd4517,dd3872,0,Lu,0,GBOther,WBALetter,SBUpper}
+static const struct unidata st304[] = {
+{dd905,0,dd3858,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd906,0,dd3859,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd907,0,dd3860,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd908,0,dd3861,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd909,0,dd3862,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd910,0,dd3863,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd911,0,dd3864,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd912,0,dd3865,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd913,dd4526,dd3866,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd914,dd4527,dd3867,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd915,dd4528,dd3868,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd916,dd4529,dd3869,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd917,dd4530,dd3870,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd918,dd4531,dd3871,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd919,dd4532,dd3872,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd920,dd4533,dd3873,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 1F70-1F7F */
-static const struct unidata st290[] = {
-{dd921,0,dd3873,0,Ll,0,GBOther,WBALetter,SBLower},
+static const struct unidata st305[] = {
+{dd921,0,dd3874,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd922,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd923,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd924,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd925,0,dd3874,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd925,0,dd3875,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd926,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd927,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd928,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -10425,165 +10825,165 @@ static const struct unidata st290[] = {
 {dd930,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd931,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd932,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd933,0,dd3875,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd933,0,dd3876,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd934,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 1F80-1F8F */
-static const struct unidata st291[] = {
-{dd935,dd4518,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd936,dd4519,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd937,dd4520,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd938,dd4521,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd939,dd4522,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd940,dd4523,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd941,dd4524,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd942,dd4525,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd943,dd4518,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd944,dd4519,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd945,dd4520,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd946,dd4521,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd947,dd4522,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd948,dd4523,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd949,dd4524,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd950,dd4525,0,0,Lt,0,GBOther,WBALetter,SBUpper}
+static const struct unidata st306[] = {
+{dd935,dd4534,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd936,dd4535,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd937,dd4536,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd938,dd4537,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd939,dd4538,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd940,dd4539,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd941,dd4540,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd942,dd4541,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd943,dd4534,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd944,dd4535,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd945,dd4536,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd946,dd4537,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd947,dd4538,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd948,dd4539,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd949,dd4540,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd950,dd4541,0,0,Lt,0,GBOther,WBALetter,SBUpper}
 };
 /* 1F90-1F9F */
-static const struct unidata st292[] = {
-{dd951,dd4526,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd952,dd4527,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd953,dd4528,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd954,dd4529,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd955,dd4530,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd956,dd4531,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd957,dd4532,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd958,dd4533,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd959,dd4526,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd960,dd4527,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd961,dd4528,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd962,dd4529,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd963,dd4530,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd964,dd4531,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd965,dd4532,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd966,dd4533,0,0,Lt,0,GBOther,WBALetter,SBUpper}
+static const struct unidata st307[] = {
+{dd951,dd4542,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd952,dd4543,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd953,dd4544,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd954,dd4545,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd955,dd4546,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd956,dd4547,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd957,dd4548,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd958,dd4549,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd959,dd4542,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd960,dd4543,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd961,dd4544,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd962,dd4545,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd963,dd4546,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd964,dd4547,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd965,dd4548,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd966,dd4549,0,0,Lt,0,GBOther,WBALetter,SBUpper}
 };
 /* 1FA0-1FAF */
-static const struct unidata st293[] = {
-{dd967,dd4534,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd968,dd4535,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd969,dd4536,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd970,dd4537,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd971,dd4538,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd972,dd4539,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd973,dd4540,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd974,dd4541,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd975,dd4534,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd976,dd4535,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd977,dd4536,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd978,dd4537,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd979,dd4538,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd980,dd4539,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd981,dd4540,0,0,Lt,0,GBOther,WBALetter,SBUpper},
-{dd982,dd4541,0,0,Lt,0,GBOther,WBALetter,SBUpper}
+static const struct unidata st308[] = {
+{dd967,dd4550,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd968,dd4551,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd969,dd4552,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd970,dd4553,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd971,dd4554,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd972,dd4555,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd973,dd4556,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd974,dd4557,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd975,dd4550,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd976,dd4551,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd977,dd4552,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd978,dd4553,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd979,dd4554,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd980,dd4555,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd981,dd4556,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd982,dd4557,0,0,Lt,0,GBOther,WBALetter,SBUpper}
 };
 /* 1FB0-1FBF */
-static const struct unidata st294[] = {
+static const struct unidata st309[] = {
 {dd983,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd984,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd985,dd4542,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd986,dd4543,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd987,dd4544,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd988,dd988,dd3876,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd989,dd4545,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd990,dd4546,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd991,dd4547,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd992,dd4548,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd993,dd4549,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd994,dd4543,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd985,dd4558,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd986,dd4559,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd987,dd4560,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd988,dd988,dd3877,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd989,dd4561,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd990,dd4562,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd991,dd4563,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd992,dd4564,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd993,dd4565,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd994,dd4559,0,0,Lt,0,GBOther,WBALetter,SBUpper},
 {dd995,0,0,0,Sk,CD,GBOther,WBOther,SBOther},
 {dd996,dd996,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd995,0,dd3877,0,Sk,CD,GBOther,WBOther,SBOther}
+{dd995,0,dd3878,0,Sk,CD,GBOther,WBOther,SBOther}
 };
 /* 1FC0-1FCF */
-static const struct unidata st295[] = {
+static const struct unidata st310[] = {
 {dd997,0,0,0,Sk,CD,GBOther,WBOther,SBOther},
 {dd998,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{dd999,dd4550,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd1000,dd4551,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd1001,dd4552,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd1002,dd1002,dd3878,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd1003,dd4553,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd1004,dd4554,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1005,dd4555,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1006,dd4556,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1007,dd4557,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1008,dd4551,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd999,dd4566,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd1000,dd4567,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd1001,dd4568,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd1002,dd1002,dd3879,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd1003,dd4569,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd1004,dd4570,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1005,dd4571,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1006,dd4572,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1007,dd4573,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1008,dd4567,0,0,Lt,0,GBOther,WBALetter,SBUpper},
 {dd1009,0,0,0,Sk,0,GBOther,WBOther,SBOther},
 {dd1010,0,0,0,Sk,0,GBOther,WBOther,SBOther},
 {dd1011,0,0,0,Sk,0,GBOther,WBOther,SBOther}
 };
 /* 1FD0-1FDF */
-static const struct unidata st296[] = {
+static const struct unidata st311[] = {
 {dd1012,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd1013,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd1014,dd4558,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd1015,dd4141,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd1014,dd4574,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd1015,dd4145,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {dd1016,dd1016,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd1017,dd4559,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd1018,dd4560,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1019,dd4561,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1020,dd4562,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1021,dd4563,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1017,dd4575,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd1018,dd4576,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1019,dd4577,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1020,dd4578,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1021,dd4579,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {dd1022,0,0,0,Sk,0,GBOther,WBOther,SBOther},
 {dd1023,0,0,0,Sk,0,GBOther,WBOther,SBOther},
 {dd1024,0,0,0,Sk,0,GBOther,WBOther,SBOther}
 };
 /* 1FE0-1FEF */
-static const struct unidata st297[] = {
+static const struct unidata st312[] = {
 {dd1025,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd1026,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd1027,dd4564,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd1028,dd4144,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd1027,dd4580,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd1028,dd4148,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
 {dd1029,dd1029,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
 {dd1030,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {dd1031,dd1031,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd1032,dd4565,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd1033,dd4566,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1034,dd4567,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1035,dd4568,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1036,dd4569,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1037,dd4570,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1032,dd4581,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd1033,dd4582,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1034,dd4583,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1035,dd4584,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1036,dd4585,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1037,dd4586,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd1038,0,0,0,Sk,0,GBOther,WBOther,SBOther},
 {dd1039,0,0,0,Sk,0,GBOther,WBOther,SBOther},
 {dd1040,0,0,0,Sk,0,GBOther,WBOther,SBOther}
 };
 /* 1FF0-1FFF */
-static const struct unidata st298[] = {
+static const struct unidata st313[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd1041,dd4571,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd1042,dd4572,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{dd1043,dd4573,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd1041,dd4587,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd1042,dd4588,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{dd1043,dd4589,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd1044,dd1044,dd3879,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd1045,dd4574,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
-{dd1046,dd4575,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1047,dd4576,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1048,dd4577,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1049,dd4578,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1050,dd4572,0,0,Lt,0,GBOther,WBALetter,SBUpper},
+{dd1044,dd1044,dd3880,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd1045,dd4590,0,0,Ll,NBC,GBOther,WBALetter,SBLower},
+{dd1046,dd4591,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1047,dd4592,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1048,dd4593,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1049,dd4594,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1050,dd4588,0,0,Lt,0,GBOther,WBALetter,SBUpper},
 {dd1051,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{dd1052,0,dd3880,0,Sk,CD,GBOther,WBOther,SBOther},
+{dd1052,0,dd3881,0,Sk,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2000-200F */
-static const struct unidata st299[] = {
+static const struct unidata st314[] = {
 {dd1053,0,0,0,Zs,0,GBOther,WBOther,SBSp},
 {dd1054,0,0,0,Zs,0,GBOther,WBOther,SBSp},
 {dd0,0,0,0,Zs,CD,GBOther,WBOther,SBSp},
@@ -10602,17 +11002,17 @@ static const struct unidata st299[] = {
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat}
 };
 /* 2010-201F */
-static const struct unidata st300[] = {
+static const struct unidata st315[] = {
 {0,0,0,0,Pd,0,GBOther,WBOther,SBOther},
 {dd1055,0,0,0,Pd,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,Pd,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Pd,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Pd,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Pd,0,GBOther,WBOther,SBSContinue},
+{0,0,0,0,Pd,0,GBOther,WBOther,SBSContinue},
 {0,0,0,0,Pd,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {dd1056,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{0,0,0,0,Pi,0,GBOther,WBOther,SBClose},
-{0,0,0,0,Pf,0,GBOther,WBMidLetter,SBClose},
+{0,0,0,0,Pi,0,GBOther,WBMidNumLet,SBClose},
+{0,0,0,0,Pf,0,GBOther,WBMidNumLet,SBClose},
 {0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Pi,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Pi,0,GBOther,WBOther,SBClose},
@@ -10621,17 +11021,17 @@ static const struct unidata st300[] = {
 {0,0,0,0,Pi,0,GBOther,WBOther,SBClose}
 };
 /* 2020-202F */
-static const struct unidata st301[] = {
+static const struct unidata st316[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{dd1057,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd1057,0,0,0,Po,CD,GBOther,WBMidNumLet,SBATerm},
 {dd1058,0,0,0,Po,CD,GBOther,WBOther,SBOther},
 {dd1059,0,0,0,Po,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBMidLetter,SBOther},
-{0,0,0,0,Zl,0,GBControl,WBOther,SBSep},
-{0,0,0,0,Zp,0,GBControl,WBOther,SBSep},
+{0,0,0,0,Zl,0,GBControl,WBNewline,SBSep},
+{0,0,0,0,Zp,0,GBControl,WBNewline,SBSep},
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
@@ -10640,7 +11040,7 @@ static const struct unidata st301[] = {
 {dd0,0,0,0,Zs,CD,GBOther,WBOther,SBSp}
 };
 /* 2030-203F */
-static const struct unidata st302[] = {
+static const struct unidata st317[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
@@ -10659,7 +11059,7 @@ static const struct unidata st302[] = {
 {0,0,0,0,Pc,0,GBOther,WBExtendNumLet,SBOther}
 };
 /* 2040-204F */
-static const struct unidata st303[] = {
+static const struct unidata st318[] = {
 {0,0,0,0,Pc,0,GBOther,WBExtendNumLet,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
@@ -10678,7 +11078,7 @@ static const struct unidata st303[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
 /* 2050-205F */
-static const struct unidata st304[] = {
+static const struct unidata st319[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -10697,12 +11097,12 @@ static const struct unidata st304[] = {
 {dd0,0,0,0,Zs,CD,GBOther,WBOther,SBSp}
 };
 /* 2060-206F */
-static const struct unidata st305[] = {
+static const struct unidata st320[] = {
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -10716,7 +11116,7 @@ static const struct unidata st305[] = {
 {0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat}
 };
 /* 2070-207F */
-static const struct unidata st306[] = {
+static const struct unidata st321[] = {
 {dd1070,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -10735,7 +11135,7 @@ static const struct unidata st306[] = {
 {dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
 /* 2080-208F */
-static const struct unidata st307[] = {
+static const struct unidata st322[] = {
 {dd1070,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd9,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd4,0,0,0,No,CD,GBOther,WBOther,SBOther},
@@ -10754,7 +11154,7 @@ static const struct unidata st307[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2090-209F */
-static const struct unidata st308[] = {
+static const struct unidata st323[] = {
 {dd2,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd517,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
 {dd10,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
@@ -10773,7 +11173,7 @@ static const struct unidata st308[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 20A0-20AF */
-static const struct unidata st309[] = {
+static const struct unidata st324[] = {
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
@@ -10792,7 +11192,7 @@ static const struct unidata st309[] = {
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther}
 };
 /* 20B0-20BF */
-static const struct unidata st310[] = {
+static const struct unidata st325[] = {
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sc,0,GBOther,WBOther,SBOther},
@@ -10811,7 +11211,7 @@ static const struct unidata st310[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 20D0-20DF */
-static const struct unidata st311[] = {
+static const struct unidata st326[] = {
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,1,Mn,0,GBExtend,WBExtend,SBExtend},
@@ -10830,7 +11230,7 @@ static const struct unidata st311[] = {
 {0,0,0,0,Me,0,GBExtend,WBExtend,SBExtend}
 };
 /* 20E0-20EF */
-static const struct unidata st312[] = {
+static const struct unidata st327[] = {
 {0,0,0,0,Me,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Me,0,GBExtend,WBExtend,SBExtend},
@@ -10848,8 +11248,27 @@ static const struct unidata st312[] = {
 {0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend}
 };
+/* 20F0-20FF */
+static const struct unidata st328[] = {
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
 /* 2100-210F */
-static const struct unidata st313[] = {
+static const struct unidata st329[] = {
 {dd1084,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1085,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
@@ -10868,7 +11287,7 @@ static const struct unidata st313[] = {
 {dd1092,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
 /* 2110-211F */
-static const struct unidata st314[] = {
+static const struct unidata st330[] = {
 {dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
@@ -10887,29 +11306,29 @@ static const struct unidata st314[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 2120-212F */
-static const struct unidata st315[] = {
+static const struct unidata st331[] = {
 {dd1095,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1096,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1097,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd1099,dd3128,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1099,dd3129,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {dd501,dd522,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{dd1100,dd3975,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{dd1100,dd3976,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
 /* 2130-213F */
-static const struct unidata st316[] = {
+static const struct unidata st332[] = {
 {dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{0,dd4579,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4595,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {dd1102,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
@@ -10925,7 +11344,7 @@ static const struct unidata st316[] = {
 {dd1108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
 /* 2140-214F */
-static const struct unidata st317[] = {
+static const struct unidata st333[] = {
 {dd1109,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -10941,10 +11360,10 @@ static const struct unidata st317[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 2150-215F */
-static const struct unidata st318[] = {
+static const struct unidata st334[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -10963,26 +11382,26 @@ static const struct unidata st318[] = {
 {dd1122,0,0,0,No,CD,GBOther,WBOther,SBOther}
 };
 /* 2160-216F */
-static const struct unidata st319[] = {
-{dd499,dd4580,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1123,dd4581,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1124,dd4582,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1125,dd4583,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1126,dd4584,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1127,dd4585,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1128,dd4586,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1129,dd4587,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1130,dd4588,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1131,dd4589,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1132,dd4590,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1133,dd4591,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd502,dd4592,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd1086,dd4593,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd494,dd4594,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
-{dd503,dd4595,0,0,Nl,CD,GBOther,WBALetter,SBUpper}
+static const struct unidata st335[] = {
+{dd499,dd4596,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1123,dd4597,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1124,dd4598,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1125,dd4599,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1126,dd4600,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1127,dd4601,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1128,dd4602,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1129,dd4603,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1130,dd4604,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1131,dd4605,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1132,dd4606,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1133,dd4607,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd502,dd4608,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd1086,dd4609,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd494,dd4610,0,0,Nl,CD,GBOther,WBALetter,SBUpper},
+{dd503,dd4611,0,0,Nl,CD,GBOther,WBALetter,SBUpper}
 };
 /* 2170-217F */
-static const struct unidata st320[] = {
+static const struct unidata st336[] = {
 {dd538,0,0,0,Nl,CD,GBOther,WBALetter,SBLower},
 {dd1134,0,0,0,Nl,CD,GBOther,WBALetter,SBLower},
 {dd1135,0,0,0,Nl,CD,GBOther,WBALetter,SBLower},
@@ -11001,16 +11420,16 @@ static const struct unidata st320[] = {
 {dd523,0,0,0,Nl,CD,GBOther,WBALetter,SBLower}
 };
 /* 2180-218F */
-static const struct unidata st321[] = {
+static const struct unidata st337[] = {
 {0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,dd4596,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4612,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -11020,12 +11439,12 @@ static const struct unidata st321[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2190-219F */
-static const struct unidata st322[] = {
-{0,0,dd3881,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+static const struct unidata st338[] = {
 {0,0,dd3882,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3883,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3884,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11039,7 +11458,7 @@ static const struct unidata st322[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 21A0-21AF */
-static const struct unidata st323[] = {
+static const struct unidata st339[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11058,7 +11477,7 @@ static const struct unidata st323[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 21C0-21CF */
-static const struct unidata st324[] = {
+static const struct unidata st340[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11077,13 +11496,13 @@ static const struct unidata st324[] = {
 {dd1148,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 21D0-21DF */
-static const struct unidata st325[] = {
-{0,0,dd3884,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,dd3885,0,Sm,0,GBOther,WBOther,SBOther},
+static const struct unidata st341[] = {
+{0,0,dd3885,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,dd3886,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,dd3887,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11096,7 +11515,7 @@ static const struct unidata st325[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 21F0-21FF */
-static const struct unidata st326[] = {
+static const struct unidata st342[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11115,26 +11534,26 @@ static const struct unidata st326[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2200-220F */
-static const struct unidata st327[] = {
+static const struct unidata st343[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3887,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3888,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1149,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3888,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3889,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1150,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3889,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3890,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1151,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2210-221F */
-static const struct unidata st328[] = {
+static const struct unidata st344[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -11153,13 +11572,13 @@ static const struct unidata st328[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2220-222F */
-static const struct unidata st329[] = {
+static const struct unidata st345[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3890,0,Sm,0,GBOther,WBOther,SBOther},
-{dd1152,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3891,0,Sm,0,GBOther,WBOther,SBOther},
+{dd1152,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3892,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1153,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -11172,7 +11591,7 @@ static const struct unidata st329[] = {
 {dd1156,0,0,0,Sm,CD,GBOther,WBOther,SBOther}
 };
 /* 2230-223F */
-static const struct unidata st330[] = {
+static const struct unidata st346[] = {
 {dd1157,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -11185,38 +11604,38 @@ static const struct unidata st330[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3892,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3893,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2240-224F */
-static const struct unidata st331[] = {
+static const struct unidata st347[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1158,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3893,0,Sm,0,GBOther,WBOther,SBOther},
-{dd1159,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3894,0,Sm,0,GBOther,WBOther,SBOther},
+{dd1159,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3895,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1160,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3895,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3896,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1161,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3896,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3897,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2260-226F */
-static const struct unidata st332[] = {
+static const struct unidata st348[] = {
 {dd1162,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3897,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3898,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1163,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3898,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3899,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3900,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -11229,34 +11648,34 @@ static const struct unidata st332[] = {
 {dd1166,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2270-227F */
-static const struct unidata st333[] = {
+static const struct unidata st349[] = {
 {dd1167,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1168,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3900,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3901,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3902,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1169,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1170,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3902,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3903,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3904,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1171,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1172,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3904,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3905,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3906,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3907,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3908,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2280-228F */
-static const struct unidata st334[] = {
+static const struct unidata st350[] = {
 {dd1173,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1174,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3908,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3909,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3910,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1175,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1176,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3910,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3911,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3912,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1177,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1178,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -11267,10 +11686,10 @@ static const struct unidata st334[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2290-229F */
-static const struct unidata st335[] = {
+static const struct unidata st351[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3912,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3913,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3914,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -11286,32 +11705,32 @@ static const struct unidata st335[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 22A0-22AF */
-static const struct unidata st336[] = {
+static const struct unidata st352[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3914,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3915,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3915,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3916,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3917,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3918,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1179,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1180,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1181,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1182,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 22B0-22BF */
-static const struct unidata st337[] = {
+static const struct unidata st353[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
-{0,0,dd3918,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3919,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3920,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,dd3921,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,dd3922,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -11324,7 +11743,7 @@ static const struct unidata st337[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 22E0-22EF */
-static const struct unidata st338[] = {
+static const struct unidata st354[] = {
 {dd1183,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1184,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {dd1185,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -11343,7 +11762,7 @@ static const struct unidata st338[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2300-230F */
-static const struct unidata st339[] = {
+static const struct unidata st355[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11362,7 +11781,7 @@ static const struct unidata st339[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 2320-232F */
-static const struct unidata st340[] = {
+static const struct unidata st356[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11381,7 +11800,7 @@ static const struct unidata st340[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 2370-237F */
-static const struct unidata st341[] = {
+static const struct unidata st357[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11400,7 +11819,7 @@ static const struct unidata st341[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 2390-239F */
-static const struct unidata st342[] = {
+static const struct unidata st358[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11419,7 +11838,7 @@ static const struct unidata st342[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 23B0-23BF */
-static const struct unidata st343[] = {
+static const struct unidata st359[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -11438,7 +11857,7 @@ static const struct unidata st343[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 23D0-23DF */
-static const struct unidata st344[] = {
+static const struct unidata st360[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11457,7 +11876,7 @@ static const struct unidata st344[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 23E0-23EF */
-static const struct unidata st345[] = {
+static const struct unidata st361[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11476,7 +11895,7 @@ static const struct unidata st345[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2420-242F */
-static const struct unidata st346[] = {
+static const struct unidata st362[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11495,7 +11914,7 @@ static const struct unidata st346[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2440-244F */
-static const struct unidata st347[] = {
+static const struct unidata st363[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11514,7 +11933,7 @@ static const struct unidata st347[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2460-246F */
-static const struct unidata st348[] = {
+static const struct unidata st364[] = {
 {dd9,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd4,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd5,0,0,0,No,CD,GBOther,WBOther,SBOther},
@@ -11533,7 +11952,7 @@ static const struct unidata st348[] = {
 {dd1199,0,0,0,No,CD,GBOther,WBOther,SBOther}
 };
 /* 2470-247F */
-static const struct unidata st349[] = {
+static const struct unidata st365[] = {
 {dd1200,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1201,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1202,0,0,0,No,CD,GBOther,WBOther,SBOther},
@@ -11552,7 +11971,7 @@ static const struct unidata st349[] = {
 {dd1215,0,0,0,No,CD,GBOther,WBOther,SBOther}
 };
 /* 2480-248F */
-static const struct unidata st350[] = {
+static const struct unidata st366[] = {
 {dd1216,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1217,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1218,0,0,0,No,CD,GBOther,WBOther,SBOther},
@@ -11571,7 +11990,7 @@ static const struct unidata st350[] = {
 {dd1231,0,0,0,No,CD,GBOther,WBOther,SBOther}
 };
 /* 2490-249F */
-static const struct unidata st351[] = {
+static const struct unidata st367[] = {
 {dd1232,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1233,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1234,0,0,0,No,CD,GBOther,WBOther,SBOther},
@@ -11590,7 +12009,7 @@ static const struct unidata st351[] = {
 {dd1247,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 24A0-24AF */
-static const struct unidata st352[] = {
+static const struct unidata st368[] = {
 {dd1248,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1249,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1250,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -11609,45 +12028,45 @@ static const struct unidata st352[] = {
 {dd1263,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 24B0-24BF */
-static const struct unidata st353[] = {
+static const struct unidata st369[] = {
 {dd1264,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1265,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1266,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1267,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1268,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1269,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd491,dd4597,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd493,dd4598,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd1086,dd4599,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd494,dd4600,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd495,dd4601,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd1101,dd4602,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd497,dd4603,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd498,dd4604,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd499,dd4605,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd500,dd4606,0,0,So,CD,GBOther,WBALetter,SBUpper}
+{dd491,dd4613,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd493,dd4614,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd1086,dd4615,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd494,dd4616,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd495,dd4617,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd1101,dd4618,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd497,dd4619,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd498,dd4620,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd499,dd4621,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd500,dd4622,0,0,So,CD,GBOther,WBALetter,SBUpper}
 };
 /* 24C0-24CF */
-static const struct unidata st354[] = {
-{dd501,dd4607,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd502,dd4608,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd503,dd4609,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd504,dd4610,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd505,dd4611,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd507,dd4612,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd1094,dd4613,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd508,dd4614,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd1270,dd4615,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd509,dd4616,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd510,dd4617,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd1126,dd4618,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd511,dd4619,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd1131,dd4620,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd1271,dd4621,0,0,So,CD,GBOther,WBALetter,SBUpper},
-{dd1098,dd4622,0,0,So,CD,GBOther,WBALetter,SBUpper}
+static const struct unidata st370[] = {
+{dd501,dd4623,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd502,dd4624,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd503,dd4625,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd504,dd4626,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd505,dd4627,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd507,dd4628,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd1094,dd4629,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd508,dd4630,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd1270,dd4631,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd509,dd4632,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd510,dd4633,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd1126,dd4634,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd511,dd4635,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd1131,dd4636,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd1271,dd4637,0,0,So,CD,GBOther,WBALetter,SBUpper},
+{dd1098,dd4638,0,0,So,CD,GBOther,WBALetter,SBUpper}
 };
 /* 24D0-24DF */
-static const struct unidata st355[] = {
+static const struct unidata st371[] = {
 {dd2,0,0,0,So,CD,GBOther,WBALetter,SBLower},
 {dd515,0,0,0,So,CD,GBOther,WBALetter,SBLower},
 {dd541,0,0,0,So,CD,GBOther,WBALetter,SBLower},
@@ -11666,7 +12085,7 @@ static const struct unidata st355[] = {
 {dd528,0,0,0,So,CD,GBOther,WBALetter,SBLower}
 };
 /* 24E0-24EF */
-static const struct unidata st356[] = {
+static const struct unidata st372[] = {
 {dd1272,0,0,0,So,CD,GBOther,WBALetter,SBLower},
 {dd287,0,0,0,So,CD,GBOther,WBALetter,SBLower},
 {dd180,0,0,0,So,CD,GBOther,WBALetter,SBLower},
@@ -11685,7 +12104,7 @@ static const struct unidata st356[] = {
 {0,0,0,0,No,0,GBOther,WBOther,SBOther}
 };
 /* 24F0-24FF */
-static const struct unidata st357[] = {
+static const struct unidata st373[] = {
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
@@ -11704,7 +12123,7 @@ static const struct unidata st357[] = {
 {0,0,0,0,No,0,GBOther,WBOther,SBOther}
 };
 /* 25B0-25BF */
-static const struct unidata st358[] = {
+static const struct unidata st374[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11723,7 +12142,7 @@ static const struct unidata st358[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 25C0-25CF */
-static const struct unidata st359[] = {
+static const struct unidata st375[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11742,7 +12161,7 @@ static const struct unidata st359[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 25F0-25FF */
-static const struct unidata st360[] = {
+static const struct unidata st376[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11761,7 +12180,7 @@ static const struct unidata st360[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2660-266F */
-static const struct unidata st361[] = {
+static const struct unidata st377[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11780,7 +12199,8 @@ static const struct unidata st361[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2690-269F */
-static const struct unidata st362[] = {
+static const struct unidata st378[] = {
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11795,15 +12215,33 @@ static const struct unidata st362[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 26B0-26BF */
-static const struct unidata st363[] = {
+static const struct unidata st379[] = {
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
+/* 26C0-26CF */
+static const struct unidata st380[] = {
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -11818,7 +12256,7 @@ static const struct unidata st363[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2700-270F */
-static const struct unidata st364[] = {
+static const struct unidata st381[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11837,7 +12275,7 @@ static const struct unidata st364[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 2720-272F */
-static const struct unidata st365[] = {
+static const struct unidata st382[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11856,7 +12294,7 @@ static const struct unidata st365[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 2740-274F */
-static const struct unidata st366[] = {
+static const struct unidata st383[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11875,7 +12313,7 @@ static const struct unidata st366[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 2750-275F */
-static const struct unidata st367[] = {
+static const struct unidata st384[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11894,7 +12332,7 @@ static const struct unidata st367[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2760-276F */
-static const struct unidata st368[] = {
+static const struct unidata st385[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11913,7 +12351,7 @@ static const struct unidata st368[] = {
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose}
 };
 /* 2770-277F */
-static const struct unidata st369[] = {
+static const struct unidata st386[] = {
 {0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
@@ -11932,7 +12370,7 @@ static const struct unidata st369[] = {
 {0,0,0,0,No,0,GBOther,WBOther,SBOther}
 };
 /* 2790-279F */
-static const struct unidata st370[] = {
+static const struct unidata st387[] = {
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,No,0,GBOther,WBOther,SBOther},
@@ -11951,7 +12389,7 @@ static const struct unidata st370[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 27B0-27BF */
-static const struct unidata st371[] = {
+static const struct unidata st388[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -11970,7 +12408,7 @@ static const struct unidata st371[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 27C0-27CF */
-static const struct unidata st372[] = {
+static const struct unidata st389[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -11983,13 +12421,13 @@ static const struct unidata st372[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 27E0-27EF */
-static const struct unidata st373[] = {
+static const struct unidata st390[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -12002,13 +12440,13 @@ static const struct unidata st373[] = {
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Pe,0,GBOther,WBOther,SBClose}
 };
 /* 2980-298F */
-static const struct unidata st374[] = {
+static const struct unidata st391[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -12027,7 +12465,7 @@ static const struct unidata st374[] = {
 {0,0,0,0,Ps,0,GBOther,WBOther,SBClose}
 };
 /* 2990-299F */
-static const struct unidata st375[] = {
+static const struct unidata st392[] = {
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
@@ -12046,7 +12484,7 @@ static const struct unidata st375[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 29D0-29DF */
-static const struct unidata st376[] = {
+static const struct unidata st393[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -12065,7 +12503,7 @@ static const struct unidata st376[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 29F0-29FF */
-static const struct unidata st377[] = {
+static const struct unidata st394[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -12084,7 +12522,7 @@ static const struct unidata st377[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2A00-2A0F */
-static const struct unidata st378[] = {
+static const struct unidata st395[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -12103,7 +12541,7 @@ static const struct unidata st378[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2A70-2A7F */
-static const struct unidata st379[] = {
+static const struct unidata st396[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -12122,7 +12560,7 @@ static const struct unidata st379[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
 /* 2AD0-2ADF */
-static const struct unidata st380[] = {
+static const struct unidata st397[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
@@ -12140,13 +12578,32 @@ static const struct unidata st380[] = {
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Sm,0,GBOther,WBOther,SBOther}
 };
-/* 2B20-2B2F */
-static const struct unidata st381[] = {
+/* 2B40-2B4F */
+static const struct unidata st398[] = {
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sm,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
+/* 2B50-2B5F */
+static const struct unidata st399[] = {
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -12160,26 +12617,7 @@ static const struct unidata st381[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2C00-2C0F */
-static const struct unidata st382[] = {
-{0,dd4623,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4624,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4625,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4626,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4627,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4628,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4629,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4630,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4631,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4632,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4633,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4634,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4635,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4636,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4637,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4638,0,0,Lu,0,GBOther,WBALetter,SBUpper}
-};
-/* 2C10-2C1F */
-static const struct unidata st383[] = {
+static const struct unidata st400[] = {
 {0,dd4639,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4640,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4641,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -12197,8 +12635,8 @@ static const struct unidata st383[] = {
 {0,dd4653,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4654,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
-/* 2C20-2C2F */
-static const struct unidata st384[] = {
+/* 2C10-2C1F */
+static const struct unidata st401[] = {
 {0,dd4655,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4656,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4657,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -12214,10 +12652,29 @@ static const struct unidata st384[] = {
 {0,dd4667,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4668,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,dd4669,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4670,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+};
+/* 2C20-2C2F */
+static const struct unidata st402[] = {
+{0,dd4671,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4672,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4673,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4674,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4675,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4676,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4677,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4678,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4679,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4680,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4681,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4682,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4683,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4684,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4685,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2C50-2C5F */
-static const struct unidata st385[] = {
+static const struct unidata st403[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -12236,85 +12693,45 @@ static const struct unidata st385[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2C60-2C6F */
-static const struct unidata st386[] = {
-{0,dd4670,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st404[] = {
+{0,dd4686,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4671,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4672,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4673,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4687,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4688,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4689,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4674,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4690,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4675,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4691,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4676,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4692,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{0,dd513,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd556,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd512,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
 /* 2C70-2C7F */
-static const struct unidata st387[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4677,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+static const struct unidata st405[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
-};
-/* 2C80-2C8F */
-static const struct unidata st388[] = {
-{0,dd4678,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4679,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4680,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4681,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4682,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4683,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4684,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4693,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4685,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
-};
-/* 2C90-2C9F */
-static const struct unidata st389[] = {
-{0,dd4686,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4687,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4694,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4688,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4689,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4690,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4691,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4692,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4693,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
+{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1126,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 2CA0-2CAF */
-static const struct unidata st390[] = {
-{0,dd4694,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+/* 2C80-2C8F */
+static const struct unidata st406[] = {
 {0,dd4695,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4696,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -12328,12 +12745,12 @@ static const struct unidata st390[] = {
 {0,dd4700,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4701,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4702,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
-/* 2CB0-2CBF */
-static const struct unidata st391[] = {
-{0,dd4702,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+/* 2C90-2C9F */
+static const struct unidata st407[] = {
 {0,dd4703,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4704,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -12347,12 +12764,12 @@ static const struct unidata st391[] = {
 {0,dd4708,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4709,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4710,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
-/* 2CC0-2CCF */
-static const struct unidata st392[] = {
-{0,dd4710,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+/* 2CA0-2CAF */
+static const struct unidata st408[] = {
 {0,dd4711,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4712,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -12366,12 +12783,12 @@ static const struct unidata st392[] = {
 {0,dd4716,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4717,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4718,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
-/* 2CD0-2CDF */
-static const struct unidata st393[] = {
-{0,dd4718,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+/* 2CB0-2CBF */
+static const struct unidata st409[] = {
 {0,dd4719,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4720,0,0,Lu,0,GBOther,WBALetter,SBUpper},
@@ -12385,13 +12802,53 @@ static const struct unidata st393[] = {
 {0,dd4724,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,dd4725,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4726,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
+};
+/* 2CC0-2CCF */
+static const struct unidata st410[] = {
+{0,dd4727,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4728,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4729,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4730,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4731,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4732,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4733,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4734,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
+};
+/* 2CD0-2CDF */
+static const struct unidata st411[] = {
+{0,dd4735,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4736,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4737,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4738,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4739,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4740,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4741,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4742,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
 /* 2CE0-2CEF */
-static const struct unidata st394[] = {
-{0,dd4726,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+static const struct unidata st412[] = {
+{0,dd4743,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,dd4727,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4744,0,0,Lu,0,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -12407,7 +12864,7 @@ static const struct unidata st394[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2CF0-2CFF */
-static const struct unidata st395[] = {
+static const struct unidata st413[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -12426,7 +12883,7 @@ static const struct unidata st395[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
 /* 2D20-2D2F */
-static const struct unidata st396[] = {
+static const struct unidata st414[] = {
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
@@ -12445,7 +12902,7 @@ static const struct unidata st396[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2D60-2D6F */
-static const struct unidata st397[] = {
+static const struct unidata st415[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -12464,7 +12921,7 @@ static const struct unidata st397[] = {
 {dd1278,0,0,0,Lm,CD,GBOther,WBALetter,SBOLetter}
 };
 /* 2DA0-2DAF */
-static const struct unidata st398[] = {
+static const struct unidata st416[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -12483,7 +12940,7 @@ static const struct unidata st398[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2E00-2E0F */
-static const struct unidata st399[] = {
+static const struct unidata st417[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Po,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Pi,0,GBOther,WBOther,SBClose},
@@ -12502,7 +12959,7 @@ static const struct unidata st399[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
 /* 2E10-2E1F */
-static const struct unidata st400[] = {
+static const struct unidata st418[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
@@ -12511,17 +12968,55 @@ static const struct unidata st400[] = {
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Pd,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Pd,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Pi,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Pf,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther}
+};
+/* 2E20-2E2F */
+static const struct unidata st419[] = {
+{0,0,0,0,Pi,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Pf,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter}
+};
+/* 2E30-2E3F */
+static const struct unidata st420[] = {
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Pi,0,GBOther,WBOther,SBClose},
-{0,0,0,0,Pf,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2E90-2E9F */
-static const struct unidata st401[] = {
+static const struct unidata st421[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -12540,7 +13035,7 @@ static const struct unidata st401[] = {
 {dd1279,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2EF0-2EFF */
-static const struct unidata st402[] = {
+static const struct unidata st422[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -12559,7 +13054,7 @@ static const struct unidata st402[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2F00-2F0F */
-static const struct unidata st403[] = {
+static const struct unidata st423[] = {
 {dd1281,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1282,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1283,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12578,7 +13073,7 @@ static const struct unidata st403[] = {
 {dd1296,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2F10-2F1F */
-static const struct unidata st404[] = {
+static const struct unidata st424[] = {
 {dd1297,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1298,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1299,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12597,7 +13092,7 @@ static const struct unidata st404[] = {
 {dd1312,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2F20-2F2F */
-static const struct unidata st405[] = {
+static const struct unidata st425[] = {
 {dd1313,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1314,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1315,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12616,7 +13111,7 @@ static const struct unidata st405[] = {
 {dd1328,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2F30-2F3F */
-static const struct unidata st406[] = {
+static const struct unidata st426[] = {
 {dd1329,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1330,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1331,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12635,7 +13130,7 @@ static const struct unidata st406[] = {
 {dd1344,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2F40-2F4F */
-static const struct unidata st407[] = {
+static const struct unidata st427[] = {
 {dd1345,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1346,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1347,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12654,7 +13149,7 @@ static const struct unidata st407[] = {
 {dd1360,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2F50-2F5F */
-static const struct unidata st408[] = {
+static const struct unidata st428[] = {
 {dd1361,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1362,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1363,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12673,7 +13168,7 @@ static const struct unidata st408[] = {
 {dd1376,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2F60-2F6F */
-static const struct unidata st409[] = {
+static const struct unidata st429[] = {
 {dd1377,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1378,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1379,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12692,7 +13187,7 @@ static const struct unidata st409[] = {
 {dd1392,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2F70-2F7F */
-static const struct unidata st410[] = {
+static const struct unidata st430[] = {
 {dd1393,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1394,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1395,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12711,7 +13206,7 @@ static const struct unidata st410[] = {
 {dd1408,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2F80-2F8F */
-static const struct unidata st411[] = {
+static const struct unidata st431[] = {
 {dd1409,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1410,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1411,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12730,7 +13225,7 @@ static const struct unidata st411[] = {
 {dd1424,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2F90-2F9F */
-static const struct unidata st412[] = {
+static const struct unidata st432[] = {
 {dd1425,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1426,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1427,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12749,7 +13244,7 @@ static const struct unidata st412[] = {
 {dd1440,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2FA0-2FAF */
-static const struct unidata st413[] = {
+static const struct unidata st433[] = {
 {dd1441,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1442,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1443,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12768,7 +13263,7 @@ static const struct unidata st413[] = {
 {dd1456,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2FB0-2FBF */
-static const struct unidata st414[] = {
+static const struct unidata st434[] = {
 {dd1457,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1458,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1459,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12787,7 +13282,7 @@ static const struct unidata st414[] = {
 {dd1472,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2FC0-2FCF */
-static const struct unidata st415[] = {
+static const struct unidata st435[] = {
 {dd1473,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1474,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1475,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12806,7 +13301,7 @@ static const struct unidata st415[] = {
 {dd1488,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 2FD0-2FDF */
-static const struct unidata st416[] = {
+static const struct unidata st436[] = {
 {dd1489,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1490,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1491,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -12825,7 +13320,7 @@ static const struct unidata st416[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 2FF0-2FFF */
-static const struct unidata st417[] = {
+static const struct unidata st437[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -12844,9 +13339,9 @@ static const struct unidata st417[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 3000-300F */
-static const struct unidata st418[] = {
+static const struct unidata st438[] = {
 {dd0,0,0,0,Zs,CD,GBOther,WBOther,SBSp},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSContinue},
 {0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -12863,7 +13358,7 @@ static const struct unidata st418[] = {
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose}
 };
 /* 3010-301F */
-static const struct unidata st419[] = {
+static const struct unidata st439[] = {
 {0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
@@ -12882,7 +13377,7 @@ static const struct unidata st419[] = {
 {0,0,0,0,Pe,0,GBOther,WBOther,SBClose}
 };
 /* 3020-302F */
-static const struct unidata st420[] = {
+static const struct unidata st440[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Nl,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Nl,0,GBOther,WBOther,SBOLetter},
@@ -12901,7 +13396,7 @@ static const struct unidata st420[] = {
 {0,0,0,224,Mn,0,GBExtend,WBExtend,SBExtend}
 };
 /* 3030-303F */
-static const struct unidata st421[] = {
+static const struct unidata st441[] = {
 {0,0,0,0,Pd,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lm,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lm,0,GBOther,WBKatakana,SBOLetter},
@@ -12920,83 +13415,83 @@ static const struct unidata st421[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 3040-304F */
-static const struct unidata st422[] = {
+static const struct unidata st442[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3922,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,dd3923,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3923,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1498,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,dd3924,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1498,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,dd3925,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1499,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3925,0,Lo,0,GBOther,WBOther,SBOLetter}
+{0,0,dd3926,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
 /* 3050-305F */
-static const struct unidata st423[] = {
+static const struct unidata st443[] = {
 {dd1500,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3926,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1501,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,dd3927,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1502,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1501,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,dd3928,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1503,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1502,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,dd3929,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1504,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1503,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,dd3930,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1505,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1504,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,dd3931,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1506,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1505,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,dd3932,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1506,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,dd3933,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1507,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3933,0,Lo,0,GBOther,WBOther,SBOLetter}
+{0,0,dd3934,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
 /* 3060-306F */
-static const struct unidata st424[] = {
+static const struct unidata st444[] = {
 {dd1508,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3934,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,dd3935,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1509,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3935,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1510,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,dd3936,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1511,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1510,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,dd3937,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1511,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,dd3938,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1512,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3938,0,Lo,0,GBOther,WBOther,SBOLetter}
+{0,0,dd3939,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
 /* 3070-307F */
-static const struct unidata st425[] = {
+static const struct unidata st445[] = {
 {dd1513,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1514,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3939,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,dd3940,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1515,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1516,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3940,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,dd3941,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1517,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1518,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3941,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,dd3942,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1519,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1520,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,dd3942,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,dd3943,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1521,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {dd1522,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
 /* 3090-309F */
-static const struct unidata st426[] = {
+static const struct unidata st446[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
@@ -13010,88 +13505,88 @@ static const struct unidata st426[] = {
 {0,0,0,8,Mn,0,GBExtend,WBExtend,SBExtend},
 {dd1524,0,0,0,Sk,CD,GBOther,WBKatakana,SBOther},
 {dd1525,0,0,0,Sk,CD,GBOther,WBKatakana,SBOther},
-{0,0,dd3943,0,Lm,0,GBOther,WBOther,SBOLetter},
+{0,0,dd3944,0,Lm,0,GBOther,WBOther,SBOLetter},
 {dd1526,0,0,0,Lm,0,GBOther,WBOther,SBOLetter},
 {dd1527,0,0,0,Lo,CD,GBOther,WBOther,SBOLetter}
 };
 /* 30A0-30AF */
-static const struct unidata st427[] = {
+static const struct unidata st447[] = {
 {0,0,0,0,Pd,0,GBOther,WBKatakana,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3944,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{0,0,dd3945,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3945,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{dd1528,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,dd3946,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{dd1528,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{0,0,dd3947,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1529,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3947,0,Lo,0,GBOther,WBKatakana,SBOLetter}
+{0,0,dd3948,0,Lo,0,GBOther,WBKatakana,SBOLetter}
 };
 /* 30B0-30BF */
-static const struct unidata st428[] = {
+static const struct unidata st448[] = {
 {dd1530,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3948,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{dd1531,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,dd3949,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{dd1532,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{dd1531,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,dd3950,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{dd1533,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{dd1532,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,dd3951,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{dd1534,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{dd1533,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,dd3952,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{dd1535,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{dd1534,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,dd3953,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{dd1536,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{dd1535,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,dd3954,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{dd1536,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{0,0,dd3955,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1537,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3955,0,Lo,0,GBOther,WBKatakana,SBOLetter}
+{0,0,dd3956,0,Lo,0,GBOther,WBKatakana,SBOLetter}
 };
 /* 30C0-30CF */
-static const struct unidata st429[] = {
+static const struct unidata st449[] = {
 {dd1538,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3956,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{0,0,dd3957,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1539,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3957,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{dd1540,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,dd3958,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{dd1541,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{dd1540,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,dd3959,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{dd1541,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{0,0,dd3960,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1542,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3960,0,Lo,0,GBOther,WBKatakana,SBOLetter}
+{0,0,dd3961,0,Lo,0,GBOther,WBKatakana,SBOLetter}
 };
 /* 30D0-30DF */
-static const struct unidata st430[] = {
+static const struct unidata st450[] = {
 {dd1543,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1544,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3961,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{0,0,dd3962,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1545,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1546,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3962,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{0,0,dd3963,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1547,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1548,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3963,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{0,0,dd3964,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1549,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1550,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3964,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{0,0,dd3965,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1551,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1552,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter}
 };
 /* 30E0-30EF */
-static const struct unidata st431[] = {
+static const struct unidata st451[] = {
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
@@ -13107,13 +13602,13 @@ static const struct unidata st431[] = {
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3965,0,Lo,0,GBOther,WBKatakana,SBOLetter}
+{0,0,dd3966,0,Lo,0,GBOther,WBKatakana,SBOLetter}
 };
 /* 30F0-30FF */
-static const struct unidata st432[] = {
-{0,0,dd3966,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+static const struct unidata st452[] = {
 {0,0,dd3967,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,dd3968,0,Lo,0,GBOther,WBKatakana,SBOLetter},
+{0,0,dd3969,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {dd1553,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
@@ -13124,12 +13619,12 @@ static const struct unidata st432[] = {
 {dd1557,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lm,0,GBOther,WBKatakana,SBOLetter},
-{0,0,dd3969,0,Lm,0,GBOther,WBKatakana,SBOLetter},
+{0,0,dd3970,0,Lm,0,GBOther,WBKatakana,SBOLetter},
 {dd1558,0,0,0,Lm,0,GBOther,WBKatakana,SBOLetter},
 {dd1559,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter}
 };
 /* 3100-310F */
-static const struct unidata st433[] = {
+static const struct unidata st453[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -13147,8 +13642,27 @@ static const struct unidata st433[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
+/* 3120-312F */
+static const struct unidata st454[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
 /* 3130-313F */
-static const struct unidata st434[] = {
+static const struct unidata st455[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {dd1560,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd1561,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
@@ -13167,7 +13681,7 @@ static const struct unidata st434[] = {
 {dd1574,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
 /* 3140-314F */
-static const struct unidata st435[] = {
+static const struct unidata st456[] = {
 {dd1575,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd1576,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd1577,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
@@ -13186,7 +13700,7 @@ static const struct unidata st435[] = {
 {dd1590,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
 /* 3150-315F */
-static const struct unidata st436[] = {
+static const struct unidata st457[] = {
 {dd1591,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd1592,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd1593,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
@@ -13205,7 +13719,7 @@ static const struct unidata st436[] = {
 {dd1606,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
 /* 3160-316F */
-static const struct unidata st437[] = {
+static const struct unidata st458[] = {
 {dd1607,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd1608,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd1609,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
@@ -13224,7 +13738,7 @@ static const struct unidata st437[] = {
 {dd1622,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
 /* 3170-317F */
-static const struct unidata st438[] = {
+static const struct unidata st459[] = {
 {dd1623,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd1624,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd1625,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
@@ -13243,7 +13757,7 @@ static const struct unidata st438[] = {
 {dd1638,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
 /* 3180-318F */
-static const struct unidata st439[] = {
+static const struct unidata st460[] = {
 {dd1639,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd1640,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd1641,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
@@ -13262,7 +13776,7 @@ static const struct unidata st439[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 3190-319F */
-static const struct unidata st440[] = {
+static const struct unidata st461[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {dd1281,0,0,0,No,CD,GBOther,WBOther,SBOther},
@@ -13281,7 +13795,7 @@ static const struct unidata st440[] = {
 {dd1289,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 31F0-31FF */
-static const struct unidata st441[] = {
+static const struct unidata st462[] = {
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter},
@@ -13300,7 +13814,7 @@ static const struct unidata st441[] = {
 {0,0,0,0,Lo,0,GBOther,WBKatakana,SBOLetter}
 };
 /* 3200-320F */
-static const struct unidata st442[] = {
+static const struct unidata st463[] = {
 {dd1664,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1665,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1666,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13319,7 +13833,7 @@ static const struct unidata st442[] = {
 {dd1679,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 3210-321F */
-static const struct unidata st443[] = {
+static const struct unidata st464[] = {
 {dd1680,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1681,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1682,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13338,7 +13852,7 @@ static const struct unidata st443[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 3220-322F */
-static const struct unidata st444[] = {
+static const struct unidata st465[] = {
 {dd1695,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1696,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1697,0,0,0,No,CD,GBOther,WBOther,SBOther},
@@ -13357,7 +13871,7 @@ static const struct unidata st444[] = {
 {dd1710,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 3230-323F */
-static const struct unidata st445[] = {
+static const struct unidata st466[] = {
 {dd1711,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1712,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1713,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13376,7 +13890,7 @@ static const struct unidata st445[] = {
 {dd1726,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 3240-324F */
-static const struct unidata st446[] = {
+static const struct unidata st467[] = {
 {dd1727,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1728,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1729,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13395,7 +13909,7 @@ static const struct unidata st446[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 3250-325F */
-static const struct unidata st447[] = {
+static const struct unidata st468[] = {
 {dd1731,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1732,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1733,0,0,0,No,CD,GBOther,WBOther,SBOther},
@@ -13414,7 +13928,7 @@ static const struct unidata st447[] = {
 {dd1746,0,0,0,No,CD,GBOther,WBOther,SBOther}
 };
 /* 3260-326F */
-static const struct unidata st448[] = {
+static const struct unidata st469[] = {
 {dd1560,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1563,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1566,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13433,7 +13947,7 @@ static const struct unidata st448[] = {
 {dd1748,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 3270-327F */
-static const struct unidata st449[] = {
+static const struct unidata st470[] = {
 {dd1749,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1750,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1751,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13452,7 +13966,7 @@ static const struct unidata st449[] = {
 {0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
 /* 3280-328F */
-static const struct unidata st450[] = {
+static const struct unidata st471[] = {
 {dd1281,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1287,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1654,0,0,0,No,CD,GBOther,WBOther,SBOther},
@@ -13471,7 +13985,7 @@ static const struct unidata st450[] = {
 {dd1312,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 3290-329F */
-static const struct unidata st451[] = {
+static const struct unidata st472[] = {
 {dd1352,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1768,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1769,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13490,7 +14004,7 @@ static const struct unidata st451[] = {
 {dd1781,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 32A0-32AF */
-static const struct unidata st452[] = {
+static const struct unidata st473[] = {
 {dd1782,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1783,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1784,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13509,7 +14023,7 @@ static const struct unidata st452[] = {
 {dd1794,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 32B0-32BF */
-static const struct unidata st453[] = {
+static const struct unidata st474[] = {
 {dd1795,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1796,0,0,0,No,CD,GBOther,WBOther,SBOther},
 {dd1797,0,0,0,No,CD,GBOther,WBOther,SBOther},
@@ -13528,7 +14042,7 @@ static const struct unidata st453[] = {
 {dd1810,0,0,0,No,CD,GBOther,WBOther,SBOther}
 };
 /* 32C0-32CF */
-static const struct unidata st454[] = {
+static const struct unidata st475[] = {
 {dd1811,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1812,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1813,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13547,167 +14061,167 @@ static const struct unidata st454[] = {
 {dd1826,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 32D0-32DF */
-static const struct unidata st455[] = {
-{dd1827,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1828,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1829,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1830,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1831,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1832,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1833,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1834,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1835,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1836,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1837,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1838,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1839,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1840,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1841,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1842,0,0,0,So,CD,GBOther,WBOther,SBOther}
+static const struct unidata st476[] = {
+{dd1827,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1828,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1829,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1830,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1831,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1832,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1833,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1834,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1835,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1836,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1837,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1838,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1839,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1840,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1841,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1842,0,0,0,So,CD,GBOther,WBKatakana,SBOther}
 };
 /* 32E0-32EF */
-static const struct unidata st456[] = {
-{dd1843,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1844,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1845,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1846,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1847,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1848,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1849,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1850,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1851,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1852,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1853,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1854,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1855,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1856,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1857,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1858,0,0,0,So,CD,GBOther,WBOther,SBOther}
+static const struct unidata st477[] = {
+{dd1843,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1844,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1845,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1846,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1847,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1848,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1849,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1850,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1851,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1852,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1853,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1854,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1855,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1856,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1857,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1858,0,0,0,So,CD,GBOther,WBKatakana,SBOther}
 };
 /* 32F0-32FF */
-static const struct unidata st457[] = {
-{dd1859,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1860,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1861,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1862,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1863,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1864,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1865,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1866,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1867,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1868,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1869,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1870,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1871,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1872,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1873,0,0,0,So,CD,GBOther,WBOther,SBOther},
+static const struct unidata st478[] = {
+{dd1859,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1860,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1861,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1862,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1863,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1864,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1865,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1866,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1867,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1868,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1869,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1870,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1871,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1872,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1873,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* 3300-330F */
-static const struct unidata st458[] = {
-{dd1874,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1875,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1876,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1877,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1878,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1879,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1880,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1881,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1882,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1883,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1884,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1885,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1886,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1887,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1888,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1889,0,0,0,So,CD,GBOther,WBOther,SBOther}
+static const struct unidata st479[] = {
+{dd1874,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1875,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1876,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1877,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1878,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1879,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1880,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1881,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1882,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1883,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1884,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1885,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1886,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1887,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1888,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1889,0,0,0,So,CD,GBOther,WBKatakana,SBOther}
 };
 /* 3310-331F */
-static const struct unidata st459[] = {
-{dd1890,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1891,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1892,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1893,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1894,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1895,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1896,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1897,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1898,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1899,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1900,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1901,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1902,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1903,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1904,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1905,0,0,0,So,CD,GBOther,WBOther,SBOther}
+static const struct unidata st480[] = {
+{dd1890,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1891,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1892,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1893,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1894,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1895,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1896,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1897,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1898,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1899,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1900,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1901,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1902,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1903,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1904,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1905,0,0,0,So,CD,GBOther,WBKatakana,SBOther}
 };
 /* 3320-332F */
-static const struct unidata st460[] = {
-{dd1906,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1907,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1908,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1909,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1910,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1911,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1912,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1913,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1914,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1915,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1916,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1917,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1918,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1919,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1920,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1921,0,0,0,So,CD,GBOther,WBOther,SBOther}
+static const struct unidata st481[] = {
+{dd1906,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1907,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1908,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1909,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1910,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1911,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1912,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1913,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1914,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1915,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1916,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1917,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1918,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1919,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1920,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1921,0,0,0,So,CD,GBOther,WBKatakana,SBOther}
 };
 /* 3330-333F */
-static const struct unidata st461[] = {
-{dd1922,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1923,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1924,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1925,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1926,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1927,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1928,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1929,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1930,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1931,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1932,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1933,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1934,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1935,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1936,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1937,0,0,0,So,CD,GBOther,WBOther,SBOther}
+static const struct unidata st482[] = {
+{dd1922,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1923,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1924,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1925,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1926,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1927,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1928,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1929,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1930,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1931,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1932,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1933,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1934,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1935,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1936,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1937,0,0,0,So,CD,GBOther,WBKatakana,SBOther}
 };
 /* 3340-334F */
-static const struct unidata st462[] = {
-{dd1938,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1939,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1940,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1941,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1942,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1943,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1944,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1945,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1946,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1947,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1948,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1949,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1950,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1951,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1952,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1953,0,0,0,So,CD,GBOther,WBOther,SBOther}
+static const struct unidata st483[] = {
+{dd1938,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1939,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1940,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1941,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1942,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1943,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1944,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1945,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1946,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1947,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1948,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1949,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1950,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1951,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1952,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1953,0,0,0,So,CD,GBOther,WBKatakana,SBOther}
 };
 /* 3350-335F */
-static const struct unidata st463[] = {
-{dd1954,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1955,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1956,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1957,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1958,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1959,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1960,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd1961,0,0,0,So,CD,GBOther,WBOther,SBOther},
+static const struct unidata st484[] = {
+{dd1954,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1955,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1956,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1957,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1958,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1959,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1960,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
+{dd1961,0,0,0,So,CD,GBOther,WBKatakana,SBOther},
 {dd1962,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1963,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1964,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13718,7 +14232,7 @@ static const struct unidata st463[] = {
 {dd1969,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 3360-336F */
-static const struct unidata st464[] = {
+static const struct unidata st485[] = {
 {dd1970,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1971,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1972,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13737,7 +14251,7 @@ static const struct unidata st464[] = {
 {dd1985,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 3370-337F */
-static const struct unidata st465[] = {
+static const struct unidata st486[] = {
 {dd1986,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1987,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd1988,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13756,7 +14270,7 @@ static const struct unidata st465[] = {
 {dd2001,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 3380-338F */
-static const struct unidata st466[] = {
+static const struct unidata st487[] = {
 {dd2002,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2003,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2004,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13775,7 +14289,7 @@ static const struct unidata st466[] = {
 {dd2017,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 3390-339F */
-static const struct unidata st467[] = {
+static const struct unidata st488[] = {
 {dd2018,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2019,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2020,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13794,7 +14308,7 @@ static const struct unidata st467[] = {
 {dd2033,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 33A0-33AF */
-static const struct unidata st468[] = {
+static const struct unidata st489[] = {
 {dd2034,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2035,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2036,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13813,7 +14327,7 @@ static const struct unidata st468[] = {
 {dd2049,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 33B0-33BF */
-static const struct unidata st469[] = {
+static const struct unidata st490[] = {
 {dd2050,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2051,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2052,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13832,7 +14346,7 @@ static const struct unidata st469[] = {
 {dd2065,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 33C0-33CF */
-static const struct unidata st470[] = {
+static const struct unidata st491[] = {
 {dd2066,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2067,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2068,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13851,7 +14365,7 @@ static const struct unidata st470[] = {
 {dd2081,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 33D0-33DF */
-static const struct unidata st471[] = {
+static const struct unidata st492[] = {
 {dd2082,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2083,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2084,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13870,7 +14384,7 @@ static const struct unidata st471[] = {
 {dd2097,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 33E0-33EF */
-static const struct unidata st472[] = {
+static const struct unidata st493[] = {
 {dd2098,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2099,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2100,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13889,7 +14403,7 @@ static const struct unidata st472[] = {
 {dd2113,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 33F0-33FF */
-static const struct unidata st473[] = {
+static const struct unidata st494[] = {
 {dd2114,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2115,0,0,0,So,CD,GBOther,WBOther,SBOther},
 {dd2116,0,0,0,So,CD,GBOther,WBOther,SBOther},
@@ -13908,7 +14422,7 @@ static const struct unidata st473[] = {
 {dd2129,0,0,0,So,CD,GBOther,WBOther,SBOther}
 };
 /* 4DB0-4DBF */
-static const struct unidata st474[] = {
+static const struct unidata st495[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
@@ -13926,16 +14440,8 @@ static const struct unidata st474[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 9FB0-9FBF */
-static const struct unidata st475[] = {
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+/* 9FC0-9FCF */
+static const struct unidata st496[] = {
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
@@ -13943,10 +14449,18 @@ static const struct unidata st475[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
 /* A010-A01F */
-static const struct unidata st476[] = {
+static const struct unidata st497[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
@@ -13964,92 +14478,149 @@ static const struct unidata st476[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* A710-A71F */
-static const struct unidata st477[] = {
-{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
-};
-/* A720-A72F */
-static const struct unidata st478[] = {
-{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
-};
-/* A800-A80F */
-static const struct unidata st479[] = {
+/* A600-A60F */
+static const struct unidata st498[] = {
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm}
 };
-/* A820-A82F */
-static const struct unidata st480[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+/* A620-A62F */
+static const struct unidata st499[] = {
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mc,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* A870-A87F */
-static const struct unidata st481[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+/* A640-A64F */
+static const struct unidata st500[] = {
+{0,dd4745,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4746,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4747,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4748,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4749,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4750,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4751,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4752,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
+};
+/* A650-A65F */
+static const struct unidata st501[] = {
+{0,dd4753,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4754,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4755,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4756,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4757,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4758,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4759,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4760,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
+};
+/* A660-A66F */
+static const struct unidata st502[] = {
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,dd4761,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4762,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4763,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4764,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4765,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4766,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend}
+};
+/* A670-A67F */
+static const struct unidata st503[] = {
+{0,0,0,0,Me,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Me,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Me,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
-{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter}
+};
+/* A680-A68F */
+static const struct unidata st504[] = {
+{0,dd4767,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4768,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4769,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4770,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4771,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4772,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4773,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4774,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
+};
+/* A690-A69F */
+static const struct unidata st505[] = {
+{0,dd4775,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4776,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4777,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4778,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -14059,107 +14630,160 @@ static const struct unidata st481[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* AC00-AC0F */
-static const struct unidata st482[] = {
-{0,0,0,0,Lo,0,GBLV,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter}
-};
-/* AC10-AC1F */
-static const struct unidata st483[] = {
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLV,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter}
+/* A710-A71F */
+static const struct unidata st506[] = {
+{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter}
 };
-/* AC20-AC2F */
-static const struct unidata st484[] = {
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter}
+/* A720-A72F */
+static const struct unidata st507[] = {
+{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,dd4779,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4780,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4781,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4782,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4783,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4784,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4785,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
-/* AC30-AC3F */
-static const struct unidata st485[] = {
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLV,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter}
+/* A730-A73F */
+static const struct unidata st508[] = {
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4786,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4787,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4788,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4789,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4790,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4791,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4792,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
-/* AC50-AC5F */
-static const struct unidata st486[] = {
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLV,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter}
+/* A740-A74F */
+static const struct unidata st509[] = {
+{0,dd4793,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4794,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4795,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4796,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4797,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4798,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4799,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4800,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
-/* D7A0-D7AF */
-static const struct unidata st487[] = {
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+/* A750-A75F */
+static const struct unidata st510[] = {
+{0,dd4801,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4802,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4803,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4804,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4805,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4806,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4807,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4808,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
+};
+/* A760-A76F */
+static const struct unidata st511[] = {
+{0,dd4809,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4810,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4811,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4812,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4813,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4814,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4815,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd2130,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
+};
+/* A770-A77F */
+static const struct unidata st512[] = {
+{dd2130,0,0,0,Lm,CD,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4816,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4817,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4818,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4819,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
+};
+/* A780-A78F */
+static const struct unidata st513[] = {
+{0,dd4820,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4821,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4822,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,dd4823,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Lm,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Sk,0,GBOther,WBOther,SBOther},
+{0,dd4824,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
+/* A7F0-A7FF */
+static const struct unidata st514[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -14171,625 +14795,363 @@ static const struct unidata st487[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* D800-D80F */
-static const struct unidata st488[] = {
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cs,0,GBOther,WBOther,SBOther}
+/* A800-A80F */
+static const struct unidata st515[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* E000-E00F */
-static const struct unidata st489[] = {
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Co,0,GBOther,WBOther,SBOther}
+/* A820-A82F */
+static const struct unidata st516[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* F900-F90F */
-static const struct unidata st490[] = {
-{dd2130,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2131,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1439,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2132,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2133,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2134,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2135,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1493,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1493,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2136,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1447,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2137,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2138,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2139,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2140,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2141,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* A870-A87F */
+static const struct unidata st517[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* F910-F91F */
-static const struct unidata st491[] = {
-{dd2142,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2143,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2144,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2145,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2146,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2147,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2148,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2149,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2150,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2151,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2152,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2153,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2154,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2155,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2156,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2157,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* F920-F92F */
-static const struct unidata st492[] = {
-{dd2158,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2159,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2160,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2161,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2162,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2163,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2164,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2165,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2166,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2167,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2168,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2169,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2170,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2171,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2172,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2173,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* A880-A88F */
+static const struct unidata st518[] = {
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* F930-F93F */
-static const struct unidata st493[] = {
-{dd2174,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2175,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2176,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2177,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1405,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2178,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2179,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2180,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2181,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2182,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2183,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2184,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2185,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2186,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2187,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2188,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* A8B0-A8BF */
+static const struct unidata st519[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
-/* F940-F94F */
-static const struct unidata st494[] = {
-{dd1478,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2189,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2190,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2191,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2192,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2193,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2194,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2195,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2196,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2197,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2198,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2199,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2200,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2201,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2202,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2203,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* A8C0-A8CF */
+static const struct unidata st520[] = {
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm}
 };
-/* F950-F95F */
-static const struct unidata st495[] = {
-{dd2204,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2205,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2206,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2207,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2208,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2209,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2210,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2211,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2212,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2213,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2214,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2215,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2146,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2216,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2217,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2218,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* A920-A92F */
+static const struct unidata st521[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm}
 };
-/* F960-F96F */
-static const struct unidata st496[] = {
-{dd2219,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2220,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2221,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2222,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2223,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2224,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2225,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2226,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2227,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2228,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2229,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2230,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2231,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2232,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2233,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2234,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* A940-A94F */
+static const struct unidata st522[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend}
 };
-/* F970-F97F */
-static const struct unidata st497[] = {
-{dd2235,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1441,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2236,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2237,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2238,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2239,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2240,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2241,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2242,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2243,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2244,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2245,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2246,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2247,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2248,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2249,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* A950-A95F */
+static const struct unidata st523[] = {
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,9,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
-/* F980-F98F */
-static const struct unidata st498[] = {
-{dd2250,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1318,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2251,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2252,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2253,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2254,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2255,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2256,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2257,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2258,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1299,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2259,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2260,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2261,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2262,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2263,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* AA20-AA2F */
+static const struct unidata st524[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend}
 };
-/* F990-F99F */
-static const struct unidata st499[] = {
-{dd2264,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2265,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2266,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2267,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2268,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2269,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2270,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2271,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2272,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2273,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2274,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2275,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2276,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2277,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2278,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2279,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* AA30-AA3F */
+static const struct unidata st525[] = {
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* F9A0-F9AF */
-static const struct unidata st500[] = {
-{dd2280,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2234,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2281,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2282,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2283,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2284,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2285,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2286,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2287,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2288,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2218,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2289,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2290,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2291,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2292,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2293,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* F9B0-F9BF */
-static const struct unidata st501[] = {
-{dd2294,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2295,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2296,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2297,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2298,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2299,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2300,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2301,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2302,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2303,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2304,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2305,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2306,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2307,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2308,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2146,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* F9C0-F9CF */
-static const struct unidata st502[] = {
-{dd2309,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2310,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2311,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2312,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1492,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2313,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2314,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2315,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2316,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2317,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2318,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2319,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2320,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2321,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2322,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2323,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* F9D0-F9DF */
-static const struct unidata st503[] = {
-{dd2324,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1765,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2325,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2326,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2327,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2328,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2329,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2330,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2331,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2332,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2333,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2220,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2334,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2335,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2336,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2337,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* F9E0-F9EF */
-static const struct unidata st504[] = {
-{dd2338,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2339,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2340,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2341,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2342,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2343,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2344,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2345,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2346,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1446,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2347,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2348,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2349,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2350,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2351,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2352,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* F9F0-F9FF */
-static const struct unidata st505[] = {
-{dd2353,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2354,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2355,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2356,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2357,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2358,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2359,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1397,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2360,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2361,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2362,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2363,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2364,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2365,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2366,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2367,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* FA00-FA0F */
-static const struct unidata st506[] = {
-{dd2368,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2369,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2370,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2371,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2372,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2373,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2374,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2375,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1424,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2376,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1427,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2377,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2378,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2379,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* FA10-FA1F */
-static const struct unidata st507[] = {
-{dd2380,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2381,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2382,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2383,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2384,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2385,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2386,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2387,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2388,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2389,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2390,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1404,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* FA20-FA2F */
-static const struct unidata st508[] = {
-{dd2391,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2392,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2393,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2394,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2395,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2396,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2397,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2398,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+/* AA40-AA4F */
+static const struct unidata st526[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mc,0,GBSpacingMark,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* FA30-FA3F */
-static const struct unidata st509[] = {
-{dd2399,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2400,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2401,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2402,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2403,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2404,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2405,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2406,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2407,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2408,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2409,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2410,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd1325,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2411,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2412,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2413,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
-};
-/* FA40-FA4F */
-static const struct unidata st510[] = {
-{dd2414,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2415,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2416,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2417,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2418,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2419,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2420,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2421,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2422,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2423,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2424,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2425,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd1770,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2426,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2427,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2428,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
-};
-/* FA50-FA5F */
-static const struct unidata st511[] = {
-{dd2429,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd1774,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2430,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2431,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2432,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2433,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2434,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2270,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2435,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2436,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2437,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2438,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2439,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2440,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2440,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2441,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
-};
-/* FA60-FA6F */
-static const struct unidata st512[] = {
-{dd2442,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2443,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2444,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2445,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2446,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2447,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2448,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2393,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2449,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2450,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2451,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+/* AA50-AA5F */
+static const struct unidata st527[] = {
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
+{0,0,0,0,Nd,0,GBOther,WBNumeric,SBNumeric},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
-};
-/* FA70-FA7F */
-static const struct unidata st513[] = {
-{dd2452,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2453,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2454,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2455,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2456,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2457,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2458,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2459,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2405,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2460,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2461,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2462,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2380,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2463,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2464,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2465,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm},
+{0,0,0,0,Po,0,GBOther,WBOther,SBSTerm}
 };
-/* FA80-FA8F */
-static const struct unidata st514[] = {
-{dd2466,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2467,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2468,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2469,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2470,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2471,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2472,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2473,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2474,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2413,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2475,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2414,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2476,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2477,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2478,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2479,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* AC00-AC0F */
+static const struct unidata st528[] = {
+{0,0,0,0,Lo,0,GBLV,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter}
 };
-/* FA90-FA9F */
-static const struct unidata st515[] = {
-{dd2480,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2381,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2167,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2481,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2482,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1358,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2235,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2318,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2483,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2484,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2421,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2485,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2422,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2486,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2487,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2488,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* AC10-AC1F */
+static const struct unidata st529[] = {
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLV,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter}
 };
-/* FAA0-FAAF */
-static const struct unidata st516[] = {
-{dd2383,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2489,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2490,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2491,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2492,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2493,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2384,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2494,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2495,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2496,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2497,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2498,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2499,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2434,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2500,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2501,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* AC20-AC2F */
+static const struct unidata st530[] = {
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter}
 };
-/* FAB0-FABF */
-static const struct unidata st517[] = {
-{dd2270,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2502,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2438,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2503,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2504,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2505,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2506,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2507,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2443,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2508,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2392,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2509,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2444,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2216,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2510,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2445,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* AC30-AC3F */
+static const struct unidata st531[] = {
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLV,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter}
 };
-/* FAC0-FACF */
-static const struct unidata st518[] = {
-{dd2511,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2447,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2512,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2513,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2514,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2515,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2516,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2449,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2389,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2517,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2450,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2518,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2451,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2519,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1493,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2520,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+/* AC50-AC5F */
+static const struct unidata st532[] = {
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLV,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter}
 };
-/* FAD0-FADF */
-static const struct unidata st519[] = {
-{dd2521,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2522,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2523,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2524,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2525,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2526,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2527,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2528,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2529,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2530,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+/* D7A0-D7AF */
+static const struct unidata st533[] = {
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBLVT,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
-};
-/* FB00-FB0F */
-static const struct unidata st520[] = {
-{dd2531,dd2531,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd2532,dd2532,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd2533,dd2533,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd2534,dd2534,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd2535,dd2535,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd2536,dd2537,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd2537,dd2537,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -14800,854 +15162,626 @@ static const struct unidata st520[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* FB10-FB1F */
-static const struct unidata st521[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2538,dd2538,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd2539,dd2539,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd2540,dd2540,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd2541,dd2541,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd2542,dd2542,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2543,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,26,Mn,0,GBExtend,WBExtend,SBExtend},
-{dd2544,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+/* D800-D80F */
+static const struct unidata st534[] = {
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cs,0,GBOther,WBOther,SBOther}
 };
-/* FB20-FB2F */
-static const struct unidata st522[] = {
-{dd2545,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd1102,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd1105,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2546,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2547,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2548,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2549,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2550,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2551,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd1077,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd2552,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2553,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2554,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2555,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2556,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2557,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+/* E000-E00F */
+static const struct unidata st535[] = {
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Co,0,GBOther,WBOther,SBOther}
 };
-/* FB30-FB3F */
-static const struct unidata st523[] = {
-{dd2558,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2559,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2560,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2561,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2562,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2563,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2564,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2565,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2566,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2567,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2568,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2569,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2570,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+/* F900-F90F */
+static const struct unidata st536[] = {
+{dd2131,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2132,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1439,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2133,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2134,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2135,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2136,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1493,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1493,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2137,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1447,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2138,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2139,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2140,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2141,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2142,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FB40-FB4F */
-static const struct unidata st524[] = {
-{dd2571,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2572,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2573,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2574,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2575,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2576,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2577,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2578,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2579,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2580,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2581,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2582,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2583,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2584,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
-};
-/* FB50-FB5F */
-static const struct unidata st525[] = {
-{dd2585,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2585,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2586,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2586,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2586,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2586,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2587,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2587,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2587,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2587,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2588,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2588,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2588,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2588,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2589,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2589,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F910-F91F */
+static const struct unidata st537[] = {
+{dd2143,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2144,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2145,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2146,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2147,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2148,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2149,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2150,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2151,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2152,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2153,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2154,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2155,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2156,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2157,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2158,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FB60-FB6F */
-static const struct unidata st526[] = {
-{dd2589,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2589,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2590,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2590,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2590,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2590,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2591,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2591,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2591,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2591,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2592,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2592,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2592,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2592,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2593,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2593,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F920-F92F */
+static const struct unidata st538[] = {
+{dd2159,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2160,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2161,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2162,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2163,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2164,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2165,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2166,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2167,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2168,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2169,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2170,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2171,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2172,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2173,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2174,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FB70-FB7F */
-static const struct unidata st527[] = {
-{dd2593,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2593,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2594,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2594,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2594,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2594,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2595,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2595,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2595,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2595,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2596,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2596,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2596,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2596,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2597,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2597,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F930-F93F */
+static const struct unidata st539[] = {
+{dd2175,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2176,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2177,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2178,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1405,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2179,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2180,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2181,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2182,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2183,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2184,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2185,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2186,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2187,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2188,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2189,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FB80-FB8F */
-static const struct unidata st528[] = {
-{dd2597,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2597,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2598,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2598,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2599,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2599,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2600,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2600,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2601,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2601,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2602,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2602,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2603,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2603,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2604,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2604,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F940-F94F */
+static const struct unidata st540[] = {
+{dd1478,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2190,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2191,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2192,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2193,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2194,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2195,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2196,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2197,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2198,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2199,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2200,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2201,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2202,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2203,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2204,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FB90-FB9F */
-static const struct unidata st529[] = {
-{dd2604,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2604,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2605,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2605,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2605,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2605,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2606,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2606,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2606,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2606,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2607,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2607,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2607,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2607,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2608,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2608,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F950-F95F */
+static const struct unidata st541[] = {
+{dd2205,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2206,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2207,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2208,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2209,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2210,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2211,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2212,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2213,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2214,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2215,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2216,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2147,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2217,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2218,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2219,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FBA0-FBAF */
-static const struct unidata st530[] = {
-{dd2609,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2609,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2609,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2609,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2610,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2610,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2611,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2611,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2611,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2611,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2612,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2612,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2612,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2612,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2613,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2613,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F960-F96F */
+static const struct unidata st542[] = {
+{dd2220,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2221,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2222,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2223,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2224,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2225,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2226,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2227,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2228,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2229,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2230,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2231,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2232,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2233,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2234,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2235,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FBB0-FBBF */
-static const struct unidata st531[] = {
-{dd2614,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2614,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+/* F970-F97F */
+static const struct unidata st543[] = {
+{dd2236,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1441,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2237,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2238,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2239,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2240,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2241,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2242,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2243,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2244,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2245,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2246,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2247,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2248,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2249,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2250,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FBD0-FBDF */
-static const struct unidata st532[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2615,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2615,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2615,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2615,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2616,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2616,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2617,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2617,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2618,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2618,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2619,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2620,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2620,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
-};
-/* FBE0-FBEF */
-static const struct unidata st533[] = {
-{dd2621,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2621,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2622,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2622,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2623,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2623,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2623,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2623,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2624,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2624,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2625,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2625,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2626,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2626,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2627,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2627,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F980-F98F */
+static const struct unidata st544[] = {
+{dd2251,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1318,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2252,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2253,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2254,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2255,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2256,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2257,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2258,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2259,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1299,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2260,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2261,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2262,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2263,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2264,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FBF0-FBFF */
-static const struct unidata st534[] = {
-{dd2628,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2628,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2629,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2629,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2630,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2630,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2631,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2631,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2631,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2632,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2632,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2632,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2633,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2633,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2633,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2633,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F990-F99F */
+static const struct unidata st545[] = {
+{dd2265,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2266,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2267,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2268,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2269,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2270,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2271,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2272,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2273,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2274,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2275,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2276,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2277,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2278,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2279,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2280,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FC00-FC0F */
-static const struct unidata st535[] = {
-{dd2634,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2635,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2636,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2632,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2637,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2638,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2639,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2640,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2641,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2642,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2643,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2644,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2645,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2646,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2647,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2648,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F9A0-F9AF */
+static const struct unidata st546[] = {
+{dd2281,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2235,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2282,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2283,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2284,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2285,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2286,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2287,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2288,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2289,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2219,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2290,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2291,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2292,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2293,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2294,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FC10-FC1F */
-static const struct unidata st536[] = {
-{dd2649,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2650,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2651,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2652,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2653,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2654,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2655,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2656,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2657,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2658,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2659,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2660,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2661,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2662,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2663,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2664,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F9B0-F9BF */
+static const struct unidata st547[] = {
+{dd2295,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2296,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2297,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2298,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2299,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2300,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2301,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2302,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2303,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2304,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2305,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2306,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2307,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2308,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2309,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2147,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FC20-FC2F */
-static const struct unidata st537[] = {
-{dd2665,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2666,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2667,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2668,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2669,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2670,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2671,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2672,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2673,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2674,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2675,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2676,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2677,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2678,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2679,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2680,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F9C0-F9CF */
+static const struct unidata st548[] = {
+{dd2310,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2311,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2312,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2313,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1492,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2314,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2315,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2316,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2317,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2318,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2319,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2320,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2321,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2322,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2323,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2324,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FC30-FC3F */
-static const struct unidata st538[] = {
-{dd2681,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2682,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2683,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2684,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2685,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2686,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2687,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2688,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2689,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2690,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2691,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2692,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2693,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2694,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2695,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2696,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F9D0-F9DF */
+static const struct unidata st549[] = {
+{dd2325,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1765,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2326,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2327,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2328,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2329,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2330,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2331,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2332,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2333,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2334,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2221,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2335,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2336,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2337,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2338,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FC40-FC4F */
-static const struct unidata st539[] = {
-{dd2697,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2698,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2699,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2700,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2701,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2702,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2703,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2704,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2705,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2706,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2707,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2708,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2709,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2710,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2711,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2712,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F9E0-F9EF */
+static const struct unidata st550[] = {
+{dd2339,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2340,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2341,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2342,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2343,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2344,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2345,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2346,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2347,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1446,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2348,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2349,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2350,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2351,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2352,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2353,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FC50-FC5F */
-static const struct unidata st540[] = {
-{dd2713,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2714,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2715,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2716,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2717,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2718,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2719,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2720,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2721,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2722,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2723,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2724,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2725,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2726,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2727,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2728,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
-};
-/* FC60-FC6F */
-static const struct unidata st541[] = {
-{dd2729,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2730,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2731,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2732,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2733,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2734,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2636,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2735,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2632,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2637,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2736,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2737,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2641,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2738,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2642,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2643,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* F9F0-F9FF */
+static const struct unidata st551[] = {
+{dd2354,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2355,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2356,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2357,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2358,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2359,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2360,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1397,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2361,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2362,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2363,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2364,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2365,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2366,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2367,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2368,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FC70-FC7F */
-static const struct unidata st542[] = {
-{dd2739,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2740,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2647,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2741,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2648,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2649,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2742,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2743,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2651,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2744,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2652,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2653,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2682,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2683,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2686,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2687,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FA00-FA0F */
+static const struct unidata st552[] = {
+{dd2369,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2370,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2371,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2372,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2373,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2374,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2375,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2376,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1424,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2377,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1427,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2378,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2379,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2380,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FC80-FC8F */
-static const struct unidata st543[] = {
-{dd2688,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2692,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2693,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2694,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2695,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2699,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2700,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2701,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2745,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2705,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2746,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2747,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2711,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2748,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2712,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2713,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FA10-FA1F */
+static const struct unidata st553[] = {
+{dd2381,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2382,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2383,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2384,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2385,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2386,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2387,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2388,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2389,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2390,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2391,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1404,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FC90-FC9F */
-static const struct unidata st544[] = {
-{dd2726,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2749,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2750,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2721,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2751,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2722,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2723,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2634,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2635,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2752,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2636,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2753,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2638,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2639,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2640,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2641,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FA20-FA2F */
+static const struct unidata st554[] = {
+{dd2392,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2393,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2394,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2395,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2396,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2397,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2398,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2399,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* FCA0-FCAF */
-static const struct unidata st545[] = {
-{dd2754,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2644,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2645,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2646,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2647,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2755,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2651,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2654,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2655,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2656,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2657,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2658,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2660,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2661,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2662,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2663,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FA30-FA3F */
+static const struct unidata st555[] = {
+{dd2400,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2401,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2402,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2403,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2404,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2405,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2406,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2407,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2408,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2409,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2410,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2411,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1325,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2412,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2413,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2414,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FCB0-FCBF */
-static const struct unidata st546[] = {
-{dd2664,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2665,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2756,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2666,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2667,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2668,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2669,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2670,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2671,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2673,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2674,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2675,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2676,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2677,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2678,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2679,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FA40-FA4F */
+static const struct unidata st556[] = {
+{dd2415,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2416,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2417,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2418,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2419,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2420,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2421,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2422,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2423,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2424,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2425,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2426,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1770,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2427,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2428,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2429,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FCC0-FCCF */
-static const struct unidata st547[] = {
-{dd2680,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2681,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2684,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2685,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2689,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2690,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2691,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2692,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2693,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2696,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2697,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2698,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2699,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2757,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2702,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2703,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FA50-FA5F */
+static const struct unidata st557[] = {
+{dd2430,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1774,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2431,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2432,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2433,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2434,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2435,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2271,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2436,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2437,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2438,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2439,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2440,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2441,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2441,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2442,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FCD0-FCDF */
-static const struct unidata st548[] = {
-{dd2704,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2705,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2708,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2709,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2710,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2711,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2758,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2714,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2715,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2759,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2718,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2719,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2720,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2721,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2760,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2636,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FA60-FA6F */
+static const struct unidata st558[] = {
+{dd2443,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2444,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2445,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2446,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2447,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2448,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2449,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2394,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2450,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2451,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2452,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* FCE0-FCEF */
-static const struct unidata st549[] = {
-{dd2753,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2641,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2754,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2647,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2755,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2651,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2761,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2664,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2762,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2763,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2764,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2692,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2693,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2699,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2711,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2758,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
-};
-/* FCF0-FCFF */
-static const struct unidata st550[] = {
-{dd2721,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2760,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2765,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2766,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2767,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2768,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2769,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2770,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2771,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2772,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2773,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2774,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2775,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2776,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2777,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2778,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FA70-FA7F */
+static const struct unidata st559[] = {
+{dd2453,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2454,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2455,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2456,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2457,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2458,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2459,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2460,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2406,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2461,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2462,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2463,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2381,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2464,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2465,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2466,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FD00-FD0F */
-static const struct unidata st551[] = {
-{dd2779,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2780,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2781,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2782,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2783,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2784,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2785,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2786,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2787,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2788,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2789,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2790,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2763,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2791,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2792,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2793,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FA80-FA8F */
+static const struct unidata st560[] = {
+{dd2467,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2468,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2469,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2470,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2471,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2472,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2473,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2474,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2475,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2414,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2476,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2415,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2477,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2478,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2479,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2480,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FD10-FD1F */
-static const struct unidata st552[] = {
-{dd2794,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2768,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2769,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2770,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2771,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2772,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2773,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2774,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2775,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2776,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2777,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2778,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2779,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2780,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2781,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2782,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FA90-FA9F */
+static const struct unidata st561[] = {
+{dd2481,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2382,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2168,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2482,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2483,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1358,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2236,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2319,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2484,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2485,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2422,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2486,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2423,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2487,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2488,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2489,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FD20-FD2F */
-static const struct unidata st553[] = {
-{dd2783,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2784,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2785,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2786,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2787,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2788,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2789,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2790,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2763,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2791,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2792,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2793,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2794,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2788,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2789,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2790,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FAA0-FAAF */
+static const struct unidata st562[] = {
+{dd2384,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2490,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2491,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2492,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2493,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2494,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2385,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2495,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2496,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2497,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2498,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2499,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2500,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2435,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2501,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2502,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FD30-FD3F */
-static const struct unidata st554[] = {
-{dd2763,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2762,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2764,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2672,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2661,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2662,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2663,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2788,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2789,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2790,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2672,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2673,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2795,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2795,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
-{0,0,0,0,Pe,0,GBOther,WBOther,SBClose}
+/* FAB0-FABF */
+static const struct unidata st563[] = {
+{dd2271,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2503,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2439,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2504,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2505,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2506,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2507,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2508,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2444,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2509,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2393,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2510,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2445,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2217,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2511,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2446,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FD50-FD5F */
-static const struct unidata st555[] = {
-{dd2796,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2797,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2797,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2798,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2799,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2800,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2801,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2802,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2803,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2803,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2804,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2805,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2806,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2807,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2808,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2809,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FAC0-FACF */
+static const struct unidata st564[] = {
+{dd2512,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2448,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2513,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2514,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2515,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2516,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2517,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2450,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2390,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2518,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2451,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2519,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2452,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2520,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1493,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2521,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-/* FD60-FD6F */
-static const struct unidata st556[] = {
-{dd2809,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2810,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2811,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2811,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2812,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2812,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2813,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2814,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2814,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2815,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2816,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2816,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2817,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2817,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2818,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2819,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+/* FAD0-FADF */
+static const struct unidata st565[] = {
+{dd2522,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2523,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2524,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2525,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2526,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2527,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2528,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2529,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2530,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2531,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* FD70-FD7F */
-static const struct unidata st557[] = {
-{dd2819,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2820,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2820,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2821,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2822,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2823,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2824,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2824,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2825,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2826,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2827,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2828,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2829,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2829,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2830,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2831,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
-};
-/* FD80-FD8F */
-static const struct unidata st558[] = {
-{dd2832,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2833,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2834,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2835,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2835,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2836,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2836,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2837,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2837,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2838,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2839,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2840,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2841,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2842,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2843,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2844,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
-};
-/* FD90-FD9F */
-static const struct unidata st559[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2845,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2846,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2847,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2848,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2849,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2850,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2850,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2851,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2852,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2853,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2854,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2854,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2855,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2856,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
-};
-/* FDA0-FDAF */
-static const struct unidata st560[] = {
-{dd2857,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2858,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2859,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2860,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2861,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2862,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2863,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2864,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2865,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2866,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2867,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2868,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2869,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2870,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2871,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2872,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
-};
-/* FDB0-FDBF */
-static const struct unidata st561[] = {
-{dd2873,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2874,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2875,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2876,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2830,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2832,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2877,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2878,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2879,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2880,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2881,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2882,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2881,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2879,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2883,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2884,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
-};
-/* FDC0-FDCF */
-static const struct unidata st562[] = {
-{dd2885,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2886,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2887,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2882,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2823,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2813,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2888,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2889,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+/* FB00-FB0F */
+static const struct unidata st566[] = {
+{dd2532,dd2532,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd2533,dd2533,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd2534,dd2534,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd2535,dd2535,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd2536,dd2536,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd2537,dd2538,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd2538,dd2538,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
-};
-/* FDF0-FDFF */
-static const struct unidata st563[] = {
-{dd2890,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2891,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2892,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2893,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2894,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2895,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2896,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2897,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2898,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2899,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2900,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2901,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2902,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
-};
-/* FE00-FE0F */
-static const struct unidata st564[] = {
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend}
-};
-/* FE10-FE1F */
-static const struct unidata st565[] = {
-{dd2903,0,0,0,Po,CD,GBOther,WBMidNum,SBOther},
-{dd2904,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2905,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2906,0,0,0,Po,CD,GBOther,WBMidNum,SBOther},
-{dd309,0,0,0,Po,CD,GBOther,WBMidNum,SBOther},
-{dd2907,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2908,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2909,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2910,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2911,0,0,0,Po,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -15655,814 +15789,795 @@ static const struct unidata st565[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* FE20-FE2F */
-static const struct unidata st566[] = {
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+/* FB10-FB1F */
+static const struct unidata st567[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd2539,dd2539,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd2540,dd2540,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd2541,dd2541,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd2542,dd2542,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd2543,dd2543,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
-};
-/* FE30-FE3F */
-static const struct unidata st567[] = {
-{dd2912,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2913,0,0,0,Pd,CD,GBOther,WBOther,SBOther},
-{dd2914,0,0,0,Pd,CD,GBOther,WBOther,SBOther},
-{dd2915,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther},
-{dd2915,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther},
-{dd1080,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd1081,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2916,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2917,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2918,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2919,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2920,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2921,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2922,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2923,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd1191,0,0,0,Ps,CD,GBOther,WBOther,SBClose}
+{dd2544,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,26,Mn,0,GBExtend,WBExtend,SBExtend},
+{dd2545,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* FE40-FE4F */
+/* FB20-FB2F */
 static const struct unidata st568[] = {
-{dd1192,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2924,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2925,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2926,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2927,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{dd2928,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2929,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2930,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2930,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2930,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2930,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2915,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther},
-{dd2915,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther},
-{dd2915,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther}
+{dd2546,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd1102,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd1105,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2547,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2548,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2549,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2550,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2551,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2552,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd1077,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd2553,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2554,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2555,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2556,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2557,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2558,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* FE50-FE5F */
+/* FB30-FB3F */
 static const struct unidata st569[] = {
-{dd2903,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2904,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd1057,0,0,0,Po,CD,GBOther,WBOther,SBSTerm},
+{dd2559,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2560,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2561,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2562,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2563,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2564,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2565,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd309,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2906,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2908,0,0,0,Po,CD,GBOther,WBOther,SBSTerm},
-{dd2907,0,0,0,Po,CD,GBOther,WBOther,SBSTerm},
-{dd2913,0,0,0,Pd,CD,GBOther,WBOther,SBOther},
-{dd1080,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd1081,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2916,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2917,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2918,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2919,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2931,0,0,0,Po,CD,GBOther,WBOther,SBOther}
+{dd2566,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2567,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2568,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2569,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2570,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd2571,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* FE60-FE6F */
+/* FB40-FB4F */
 static const struct unidata st570[] = {
-{dd2932,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2933,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd1077,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd2934,0,0,0,Pd,CD,GBOther,WBOther,SBOther},
-{dd2935,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd2936,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd1079,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2937,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2938,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
-{dd2939,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2940,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd2572,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2573,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd2574,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2575,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2576,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2577,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2578,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2579,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2580,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2581,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2582,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2583,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2584,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2585,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FE70-FE7F */
+/* FB50-FB5F */
 static const struct unidata st571[] = {
-{dd2941,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2942,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2943,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{dd2944,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2945,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2946,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2947,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2948,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2949,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2950,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2951,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2952,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2953,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2954,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+{dd2586,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2586,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2587,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2587,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2587,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2587,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2588,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2588,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2588,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2588,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2589,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2589,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2589,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2589,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2590,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2590,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FE80-FE8F */
+/* FB60-FB6F */
 static const struct unidata st572[] = {
-{dd2955,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2956,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2956,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2957,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2957,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2958,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2958,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2959,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2959,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2960,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2960,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2960,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2960,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2961,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2961,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2962,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+{dd2590,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2590,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2591,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2591,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2591,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2591,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2592,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2592,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2592,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2592,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2593,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2593,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2593,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2593,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2594,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2594,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FE90-FE9F */
+/* FB70-FB7F */
 static const struct unidata st573[] = {
-{dd2962,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2962,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2962,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2963,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2963,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2964,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2964,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2964,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2964,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2965,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2965,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2965,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2965,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2966,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2966,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2966,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+{dd2594,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2594,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2595,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2595,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2595,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2595,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2596,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2596,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2596,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2596,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2597,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2597,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2597,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2597,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2598,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2598,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FEA0-FEAF */
+/* FB80-FB8F */
 static const struct unidata st574[] = {
-{dd2966,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2967,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2967,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2967,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2967,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2968,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2968,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2968,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2968,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2969,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2969,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2970,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2970,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2971,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2971,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2972,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+{dd2598,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2598,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2599,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2599,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2600,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2600,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2601,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2601,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2602,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2602,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2603,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2603,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2604,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2604,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2605,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2605,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FEB0-FEBF */
+/* FB90-FB9F */
 static const struct unidata st575[] = {
-{dd2972,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2973,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2973,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2973,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2973,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2974,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2974,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2974,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2974,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2975,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2975,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2975,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2975,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2976,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2976,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2976,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+{dd2605,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2605,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2606,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2606,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2606,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2606,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2607,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2607,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2607,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2607,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2608,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2608,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2608,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2608,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2609,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2609,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FEC0-FECF */
+/* FBA0-FBAF */
 static const struct unidata st576[] = {
-{dd2976,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2977,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2977,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2977,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2977,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2978,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2978,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2978,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2978,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2979,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2979,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2979,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2979,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2980,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2980,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2980,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+{dd2610,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2610,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2610,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2610,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2611,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2611,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2612,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2612,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2612,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2612,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2613,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2613,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2613,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2613,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2614,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2614,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FED0-FEDF */
+/* FBB0-FBBF */
 static const struct unidata st577[] = {
-{dd2980,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2981,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2981,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2981,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2981,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2982,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2982,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2982,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2982,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2983,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2983,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2983,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2983,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2984,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2984,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2984,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+{dd2615,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2615,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* FEE0-FEEF */
+/* FBD0-FBDF */
 static const struct unidata st578[] = {
-{dd2984,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2985,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2985,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2985,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2985,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2986,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2986,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2986,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2986,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2987,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2987,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2987,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2987,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2988,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2988,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2624,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd2616,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2616,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2616,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2616,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2617,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2617,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2618,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2618,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2619,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2619,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2620,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2621,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2621,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FEF0-FEFF */
+/* FBE0-FBEF */
 static const struct unidata st579[] = {
+{dd2622,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2622,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2623,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2623,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {dd2624,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2989,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2989,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2989,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2989,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2990,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2990,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2991,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2991,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2992,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2992,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2993,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd2993,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat}
+{dd2624,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2624,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2624,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2625,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2625,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2626,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2626,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2627,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2627,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2628,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2628,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FF00-FF0F */
+/* FBF0-FBFF */
 static const struct unidata st580[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2907,0,0,0,Po,CD,GBOther,WBOther,SBSTerm},
-{dd2994,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2931,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2938,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
-{dd2939,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2932,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2995,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd1080,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd1081,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2933,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd1077,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd2903,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2934,0,0,0,Pd,CD,GBOther,WBOther,SBOther},
-{dd1057,0,0,0,Po,CD,GBOther,WBOther,SBSTerm},
-{dd2996,0,0,0,Po,CD,GBOther,WBOther,SBOther}
+{dd2629,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2629,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2630,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2630,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2631,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2631,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2632,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2632,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2632,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2633,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2633,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2633,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2634,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2634,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2634,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2634,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FF10-FF1F */
+/* FC00-FC0F */
 static const struct unidata st581[] = {
-{dd1070,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
-{dd9,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
-{dd4,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
-{dd5,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
-{dd1071,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
-{dd1072,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
-{dd1073,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
-{dd1074,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
-{dd1075,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
-{dd1076,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
-{dd2906,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd309,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2935,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd1079,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd2936,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd2908,0,0,0,Po,CD,GBOther,WBOther,SBSTerm}
+{dd2635,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2636,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2637,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2633,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2638,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2639,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2640,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2641,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2642,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2643,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2644,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2645,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2646,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2647,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2648,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2649,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FF20-FF2F */
+/* FC10-FC1F */
 static const struct unidata st582[] = {
-{dd2940,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd491,dd4728,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd493,dd4729,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1086,dd4730,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd494,dd4731,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd495,dd4732,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1101,dd4733,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd497,dd4734,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd498,dd4735,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd499,dd4736,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd500,dd4737,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd501,dd4738,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd502,dd4739,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd503,dd4740,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd504,dd4741,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd505,dd4742,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{dd2650,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2651,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2652,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2653,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2654,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2655,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2656,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2657,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2658,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2659,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2660,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2661,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2662,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2663,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2664,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2665,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FF30-FF3F */
+/* FC20-FC2F */
 static const struct unidata st583[] = {
-{dd507,dd4743,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1094,dd4744,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd508,dd4745,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1270,dd4746,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd509,dd4747,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd510,dd4748,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1126,dd4749,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd511,dd4750,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1131,dd4751,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1271,dd4752,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1098,dd4753,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd2928,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2937,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd2929,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2997,0,0,0,Sk,CD,GBOther,WBOther,SBOther},
-{dd2915,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther}
+{dd2666,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2667,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2668,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2669,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2670,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2671,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2672,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2673,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2674,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2675,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2676,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2677,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2678,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2679,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2680,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2681,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FF40-FF4F */
+/* FC30-FC3F */
 static const struct unidata st584[] = {
-{dd1040,0,0,0,Sk,CD,GBOther,WBOther,SBOther},
-{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{dd2682,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2683,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2684,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2685,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2686,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2687,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2688,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2689,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2690,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2691,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2692,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2693,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2694,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2695,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2696,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2697,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FF50-FF5F */
+/* FC40-FC4F */
 static const struct unidata st585[] = {
-{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd2916,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2998,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd2917,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2999,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3000,0,0,0,Ps,CD,GBOther,WBOther,SBClose}
+{dd2698,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2699,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2700,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2701,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2702,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2703,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2704,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2705,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2706,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2707,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2708,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2709,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2710,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2711,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2712,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2713,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FF60-FF6F */
+/* FC50-FC5F */
 static const struct unidata st586[] = {
-{dd3001,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2905,0,0,0,Po,CD,GBOther,WBOther,SBSTerm},
-{dd2924,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
-{dd2925,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
-{dd2904,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd3002,0,0,0,Po,CD,GBOther,WBOther,SBOther},
-{dd1873,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd3003,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd3004,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd3005,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd3006,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd3007,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd3008,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd3009,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd3010,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd3011,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter}
+{dd2714,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2715,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2716,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2717,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2718,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2719,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2720,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2721,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2722,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2723,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2724,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2725,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2726,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2727,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2728,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2729,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FF70-FF7F */
+/* FC60-FC6F */
 static const struct unidata st587[] = {
-{dd3012,0,0,0,Lm,CD,GBOther,WBKatakana,SBOLetter},
-{dd1827,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1828,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1829,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1830,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1831,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1832,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1833,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1834,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1835,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1836,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1837,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1838,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1839,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1840,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1841,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter}
+{dd2730,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2731,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2732,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2733,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2734,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2735,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2637,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2736,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2633,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2638,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2737,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2738,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2642,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2739,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2643,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2644,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FF80-FF8F */
+/* FC70-FC7F */
 static const struct unidata st588[] = {
-{dd1842,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1843,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1844,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1845,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1846,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1847,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1848,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1849,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1850,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1851,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1852,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1853,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1854,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1855,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1856,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1857,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter}
+{dd2740,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2741,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2648,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2742,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2649,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2650,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2743,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2744,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2652,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2745,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2653,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2654,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2683,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2684,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2687,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2688,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FF90-FF9F */
+/* FC80-FC8F */
 static const struct unidata st589[] = {
-{dd1858,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1859,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1860,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1861,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1862,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1863,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1864,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1865,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1866,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1867,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1868,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1869,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd1870,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd3013,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
-{dd3014,0,0,0,Lm,CD,GBOther,WBKatakana,SBOLetter},
-{dd3015,0,0,0,Lm,CD,GBOther,WBKatakana,SBOLetter}
-};
-/* FFA0-FFAF */
-static const struct unidata st590[] = {
-{dd3016,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3017,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3018,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3019,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3020,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3021,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3022,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3023,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3024,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3025,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3026,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3027,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3028,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3029,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3030,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3031,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+{dd2689,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2693,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2694,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2695,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2696,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2700,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2701,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2702,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2746,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2706,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2747,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2748,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2712,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2749,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2713,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2714,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FFB0-FFBF */
+/* FC90-FC9F */
+static const struct unidata st590[] = {
+{dd2727,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2750,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2751,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2722,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2752,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2723,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2724,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2635,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2636,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2753,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2637,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2754,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2639,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2640,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2641,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2642,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+};
+/* FCA0-FCAF */
 static const struct unidata st591[] = {
-{dd3032,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3033,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3034,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3035,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3036,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3037,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3038,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3039,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3040,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3041,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3042,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3043,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3044,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3045,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3046,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2755,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2645,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2646,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2647,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2648,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2756,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2652,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2655,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2656,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2657,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2658,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2659,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2661,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2662,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2663,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2664,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FFC0-FFCF */
+/* FCB0-FCBF */
 static const struct unidata st592[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd3047,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3048,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3049,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3050,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3051,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3052,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd3053,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3054,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3055,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3056,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3057,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3058,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+{dd2665,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2666,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2757,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2667,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2668,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2669,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2670,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2671,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2672,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2674,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2675,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2676,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2677,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2678,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2679,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2680,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FFD0-FFDF */
+/* FCC0-FCCF */
 static const struct unidata st593[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd3059,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3060,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3061,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3062,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3063,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3064,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd3065,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3066,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{dd3067,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2681,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2682,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2685,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2686,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2690,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2691,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2692,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2693,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2694,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2697,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2698,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2699,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2700,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2758,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2703,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2704,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FFE0-FFEF */
+/* FCD0-FCDF */
 static const struct unidata st594[] = {
-{dd3068,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
-{dd3069,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
-{dd3070,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3071,0,0,0,Sk,CD,GBOther,WBOther,SBOther},
-{dd3072,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd3073,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
-{dd3074,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd3075,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd3076,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3077,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3078,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3079,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3080,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{dd3081,0,0,0,So,CD,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2705,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2706,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2709,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2710,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2711,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2712,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2759,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2715,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2716,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2760,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2719,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2720,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2721,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2722,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2761,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2637,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* FFF0-FFFF */
+/* FCE0-FCEF */
 static const struct unidata st595[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2754,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2642,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2755,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2648,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2756,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2652,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2762,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2665,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2763,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2764,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2765,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2693,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2694,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2700,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2712,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2759,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10000-1000F */
+/* FCF0-FCFF */
 static const struct unidata st596[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+{dd2722,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2761,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2766,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2767,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2768,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2769,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2770,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2771,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2772,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2773,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2774,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2775,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2776,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2777,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2778,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2779,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10030-1003F */
+/* FD00-FD0F */
 static const struct unidata st597[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
-};
-/* 10100-1010F */
-static const struct unidata st598[] = {
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther}
+{dd2780,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2781,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2782,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2783,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2784,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2785,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2786,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2787,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2788,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2789,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2790,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2791,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2764,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2792,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2793,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2794,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10130-1013F */
+/* FD10-FD1F */
+static const struct unidata st598[] = {
+{dd2795,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2769,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2770,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2771,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2772,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2773,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2774,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2775,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2776,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2777,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2778,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2779,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2780,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2781,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2782,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2783,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+};
+/* FD20-FD2F */
 static const struct unidata st599[] = {
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther}
+{dd2784,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2785,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2786,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2787,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2788,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2789,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2790,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2791,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2764,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2792,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2793,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2794,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2795,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2789,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2790,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2791,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10140-1014F */
+/* FD30-FD3F */
 static const struct unidata st600[] = {
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter}
+{dd2764,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2763,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2765,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2673,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2662,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2663,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2664,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2789,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2790,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2791,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2673,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2674,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2796,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2796,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Ps,0,GBOther,WBOther,SBClose},
+{0,0,0,0,Pe,0,GBOther,WBOther,SBClose}
 };
-/* 10170-1017F */
+/* FD50-FD5F */
 static const struct unidata st601[] = {
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther}
+{dd2797,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2798,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2798,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2799,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2800,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2801,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2802,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2803,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2804,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2804,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2805,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2806,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2807,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2808,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2809,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2810,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10180-1018F */
+/* FD60-FD6F */
 static const struct unidata st602[] = {
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2810,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2811,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2812,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2812,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2813,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2813,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2814,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2815,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2815,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2816,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2817,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2817,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2818,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2818,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2819,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2820,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10310-1031F */
+/* FD70-FD7F */
 static const struct unidata st603[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2820,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2821,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2821,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2822,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2823,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2824,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2825,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2825,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2826,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2827,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2828,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2829,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2830,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2830,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2831,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2832,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10320-1032F */
+/* FD80-FD8F */
 static const struct unidata st604[] = {
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2833,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2834,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2835,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2836,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2836,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2837,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2837,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2838,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2838,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2839,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2840,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2841,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2842,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2843,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2844,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2845,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10340-1034F */
+/* FD90-FD9F */
 static const struct unidata st605[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2846,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2847,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2848,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2849,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2850,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2851,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2851,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2852,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2853,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2854,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2855,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2855,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2856,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2857,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10390-1039F */
+/* FDA0-FDAF */
 static const struct unidata st606[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther}
+{dd2858,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2859,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2860,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2861,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2862,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2863,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2864,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2865,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2866,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2867,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2868,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2869,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2870,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2871,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2872,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2873,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 103C0-103CF */
+/* FDB0-FDBF */
 static const struct unidata st607[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+{dd2874,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2875,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2876,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2877,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2831,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2833,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2878,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2879,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2880,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2881,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2882,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2883,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2882,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2880,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2884,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2885,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 103D0-103DF */
+/* FDC0-FDCF */
 static const struct unidata st608[] = {
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd2886,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2887,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2888,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2883,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2824,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2814,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2889,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2890,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -16472,469 +16587,597 @@ static const struct unidata st608[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 10400-1040F */
+/* FDF0-FDFF */
 static const struct unidata st609[] = {
-{0,dd4754,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4755,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4756,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4757,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4758,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4759,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4760,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4761,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4762,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4763,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4764,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4765,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4766,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4767,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4768,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4769,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+{dd2891,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2892,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2893,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2894,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2895,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2896,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2897,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2898,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2899,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2900,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2901,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2902,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2903,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 10410-1041F */
+/* FE00-FE0F */
 static const struct unidata st610[] = {
-{0,dd4770,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4771,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4772,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4773,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4774,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4775,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4776,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4777,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4778,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4779,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4780,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4781,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4782,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4783,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4784,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4785,0,0,Lu,0,GBOther,WBALetter,SBUpper}
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend}
 };
-/* 10420-1042F */
+/* FE10-FE1F */
 static const struct unidata st611[] = {
-{0,dd4786,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4787,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4788,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4789,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4790,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4791,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4792,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,dd4793,0,0,Lu,0,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
-{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
-};
-/* 10800-1080F */
-static const struct unidata st612[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2904,0,0,0,Po,CD,GBOther,WBMidNum,SBSContinue},
+{dd2905,0,0,0,Po,CD,GBOther,WBOther,SBSContinue},
+{dd2906,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2907,0,0,0,Po,CD,GBOther,WBMidLetter,SBSContinue},
+{dd309,0,0,0,Po,CD,GBOther,WBMidNum,SBOther},
+{dd2908,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2909,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2910,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2911,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2912,0,0,0,Po,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 10830-1083F */
+/* FE30-FE3F */
+static const struct unidata st612[] = {
+{dd2913,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2914,0,0,0,Pd,CD,GBOther,WBOther,SBSContinue},
+{dd2915,0,0,0,Pd,CD,GBOther,WBOther,SBSContinue},
+{dd2916,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther},
+{dd2916,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther},
+{dd1080,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd1081,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2917,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2918,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2919,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2920,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2921,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2922,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2923,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2924,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd1191,0,0,0,Ps,CD,GBOther,WBOther,SBClose}
+};
+/* FE40-FE4F */
 static const struct unidata st613[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd1192,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2925,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2926,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2927,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2928,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{dd2929,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2930,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2931,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2931,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2931,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2931,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2916,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther},
+{dd2916,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther},
+{dd2916,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther}
+};
+/* FE50-FE5F */
+static const struct unidata st614[] = {
+{dd2904,0,0,0,Po,CD,GBOther,WBMidNum,SBSContinue},
+{dd2905,0,0,0,Po,CD,GBOther,WBOther,SBSContinue},
+{dd1057,0,0,0,Po,CD,GBOther,WBMidNumLet,SBATerm},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd309,0,0,0,Po,CD,GBOther,WBMidNum,SBOther},
+{dd2907,0,0,0,Po,CD,GBOther,WBMidLetter,SBSContinue},
+{dd2909,0,0,0,Po,CD,GBOther,WBOther,SBSTerm},
+{dd2908,0,0,0,Po,CD,GBOther,WBOther,SBSTerm},
+{dd2914,0,0,0,Pd,CD,GBOther,WBOther,SBSContinue},
+{dd1080,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd1081,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2917,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2918,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2919,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2920,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2932,0,0,0,Po,CD,GBOther,WBOther,SBOther}
+};
+/* FE60-FE6F */
+static const struct unidata st615[] = {
+{dd2933,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2934,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd1077,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd2935,0,0,0,Pd,CD,GBOther,WBOther,SBSContinue},
+{dd2936,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd2937,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd1079,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd2938,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2939,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
+{dd2940,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2941,0,0,0,Po,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 10910-1091F */
-static const struct unidata st614[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther}
-};
-/* 10A00-10A0F */
-static const struct unidata st615[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend}
-};
-/* 10A10-10A1F */
-static const struct unidata st616[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+/* FE70-FE7F */
+static const struct unidata st616[] = {
+{dd2942,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2943,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2944,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{dd2945,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
+{dd2946,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2947,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2948,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2949,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2950,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2951,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2952,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2953,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2954,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2955,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10A30-10A3F */
+/* FE80-FE8F */
 static const struct unidata st617[] = {
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,1,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend}
+{dd2956,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2957,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2957,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2958,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2958,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2959,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2959,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2960,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2960,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2961,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2961,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2961,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2961,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2962,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2962,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2963,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10A40-10A4F */
+/* FE90-FE9F */
 static const struct unidata st618[] = {
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2963,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2963,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2963,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2964,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2964,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2965,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2965,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2965,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2965,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2966,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2966,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2966,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2966,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2967,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2967,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2967,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 10A50-10A5F */
+/* FEA0-FEAF */
 static const struct unidata st619[] = {
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2967,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2968,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2968,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2968,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2968,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2969,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2969,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2969,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2969,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2970,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2970,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2971,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2971,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2972,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2972,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2973,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 12460-1246F */
+/* FEB0-FEBF */
 static const struct unidata st620[] = {
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2973,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2974,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2974,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2974,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2974,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2975,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2975,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2975,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2975,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2976,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2976,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2976,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2976,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2977,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2977,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2977,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 12470-1247F */
+/* FEC0-FECF */
 static const struct unidata st621[] = {
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2977,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2978,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2978,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2978,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2978,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2979,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2979,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2979,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2979,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2980,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2980,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2980,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2980,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2981,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2981,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2981,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 1D0F0-1D0FF */
+/* FED0-FEDF */
 static const struct unidata st622[] = {
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd2981,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2982,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2982,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2982,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2982,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2983,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2983,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2983,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2983,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2984,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2984,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2984,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2984,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2985,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2985,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2985,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 1D120-1D12F */
+/* FEE0-FEEF */
 static const struct unidata st623[] = {
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther}
+{dd2985,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2986,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2986,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2986,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2986,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2987,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2987,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2987,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2987,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2988,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2988,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2988,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2988,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2989,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2989,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2625,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
 };
-/* 1D150-1D15F */
+/* FEF0-FEFF */
 static const struct unidata st624[] = {
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd3082,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd3083,0,0,0,So,0,GBOther,WBOther,SBOther}
+{dd2625,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2990,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2990,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2990,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2990,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2991,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2991,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2992,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2992,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2993,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2993,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2994,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd2994,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat}
 };
-/* 1D160-1D16F */
+/* FF00-FF0F */
 static const struct unidata st625[] = {
-{dd3084,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd3085,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd3086,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd3087,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd3088,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,216,Mc,0,GBOther,WBOther,SBOther},
-{0,0,0,1,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,1,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,1,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,226,Mc,0,GBOther,WBOther,SBOther},
-{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend}
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd2908,0,0,0,Po,CD,GBOther,WBOther,SBSTerm},
+{dd2995,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2932,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2939,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
+{dd2940,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2933,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2996,0,0,0,Po,CD,GBOther,WBMidNumLet,SBOther},
+{dd1080,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd1081,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2934,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd1077,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd2904,0,0,0,Po,CD,GBOther,WBMidNum,SBSContinue},
+{dd2935,0,0,0,Pd,CD,GBOther,WBOther,SBSContinue},
+{dd1057,0,0,0,Po,CD,GBOther,WBMidNumLet,SBATerm},
+{dd2997,0,0,0,Po,CD,GBOther,WBOther,SBOther}
 };
-/* 1D170-1D17F */
+/* FF10-FF1F */
 static const struct unidata st626[] = {
-{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend}
+{dd1070,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
+{dd9,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
+{dd4,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
+{dd5,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
+{dd1071,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
+{dd1072,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
+{dd1073,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
+{dd1074,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
+{dd1075,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
+{dd1076,0,0,0,Nd,CD,GBOther,WBOther,SBOther},
+{dd2907,0,0,0,Po,CD,GBOther,WBMidLetter,SBSContinue},
+{dd309,0,0,0,Po,CD,GBOther,WBMidNum,SBOther},
+{dd2936,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd1079,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd2937,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd2909,0,0,0,Po,CD,GBOther,WBOther,SBSTerm}
 };
-/* 1D180-1D18F */
+/* FF20-FF2F */
 static const struct unidata st627[] = {
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther}
+{dd2941,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd491,dd4825,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd493,dd4826,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1086,dd4827,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd494,dd4828,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd495,dd4829,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1101,dd4830,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd497,dd4831,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd498,dd4832,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd499,dd4833,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd500,dd4834,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd501,dd4835,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd502,dd4836,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd503,dd4837,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd504,dd4838,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd505,dd4839,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 1D1A0-1D1AF */
+/* FF30-FF3F */
 static const struct unidata st628[] = {
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther}
+{dd507,dd4840,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1094,dd4841,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd508,dd4842,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1270,dd4843,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd509,dd4844,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd510,dd4845,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1126,dd4846,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd511,dd4847,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1131,dd4848,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1271,dd4849,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1098,dd4850,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd2929,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2938,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd2930,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2998,0,0,0,Sk,CD,GBOther,WBOther,SBOther},
+{dd2916,0,0,0,Pc,CD,GBOther,WBExtendNumLet,SBOther}
 };
-/* 1D1B0-1D1BF */
+/* FF40-FF4F */
 static const struct unidata st629[] = {
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd3089,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd3090,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd3091,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd3092,0,0,0,So,0,GBOther,WBOther,SBOther},
-{dd3093,0,0,0,So,0,GBOther,WBOther,SBOther}
+{dd1040,0,0,0,Sk,CD,GBOther,WBOther,SBOther},
+{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 1D1C0-1D1CF */
+/* FF50-FF5F */
 static const struct unidata st630[] = {
-{dd3094,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther}
+{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd2917,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2999,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd2918,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd3000,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3001,0,0,0,Ps,CD,GBOther,WBOther,SBClose}
 };
-/* 1D1D0-1D1DF */
+/* FF60-FF6F */
 static const struct unidata st631[] = {
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd3002,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2906,0,0,0,Po,CD,GBOther,WBOther,SBSTerm},
+{dd2925,0,0,0,Ps,CD,GBOther,WBOther,SBClose},
+{dd2926,0,0,0,Pe,CD,GBOther,WBOther,SBClose},
+{dd2905,0,0,0,Po,CD,GBOther,WBOther,SBSContinue},
+{dd3003,0,0,0,Po,CD,GBOther,WBOther,SBOther},
+{dd1873,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd3004,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd3005,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd3006,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd3007,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd3008,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd3009,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd3010,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd3011,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd3012,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter}
 };
-/* 1D240-1D24F */
+/* FF70-FF7F */
 static const struct unidata st632[] = {
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
-{0,0,0,0,So,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+{dd3013,0,0,0,Lm,CD,GBOther,WBKatakana,SBOLetter},
+{dd1827,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1828,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1829,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1830,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1831,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1832,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1833,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1834,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1835,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1836,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1837,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1838,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1839,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1840,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1841,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter}
 };
-/* 1D370-1D37F */
+/* FF80-FF8F */
 static const struct unidata st633[] = {
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
-{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{dd1842,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1843,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1844,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1845,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1846,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1847,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1848,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1849,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1850,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1851,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1852,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1853,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1854,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1855,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1856,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1857,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter}
+};
+/* FF90-FF9F */
+static const struct unidata st634[] = {
+{dd1858,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1859,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1860,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1861,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1862,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1863,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1864,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1865,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1866,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1867,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1868,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1869,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd1870,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd3014,0,0,0,Lo,CD,GBOther,WBKatakana,SBOLetter},
+{dd3015,0,0,0,Lm,CD,GBExtend,WBExtend,SBExtend},
+{dd3016,0,0,0,Lm,CD,GBExtend,WBExtend,SBExtend}
+};
+/* FFA0-FFAF */
+static const struct unidata st635[] = {
+{dd3017,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3018,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3019,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3020,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3021,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3022,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3023,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3024,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3025,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3026,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3027,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3028,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3029,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3030,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3031,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3032,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+};
+/* FFB0-FFBF */
+static const struct unidata st636[] = {
+{dd3033,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3034,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3035,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3036,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3037,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3038,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3039,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3040,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3041,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3042,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3043,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3044,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3045,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3046,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3047,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
+/* FFC0-FFCF */
+static const struct unidata st637[] = {
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd3048,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3049,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3050,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3051,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3052,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3053,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd3054,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3055,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3056,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3057,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3058,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3059,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter}
+};
+/* FFD0-FFDF */
+static const struct unidata st638[] = {
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd3060,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3061,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3062,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3063,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3064,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3065,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd3066,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3067,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
+{dd3068,0,0,0,Lo,CD,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
+/* FFE0-FFEF */
+static const struct unidata st639[] = {
+{dd3069,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
+{dd3070,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
+{dd3071,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3072,0,0,0,Sk,CD,GBOther,WBOther,SBOther},
+{dd3073,0,0,0,So,CD,GBOther,WBOther,SBOther},
+{dd3074,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
+{dd3075,0,0,0,Sc,CD,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd3076,0,0,0,So,CD,GBOther,WBOther,SBOther},
+{dd3077,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3078,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3079,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3080,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3081,0,0,0,So,CD,GBOther,WBOther,SBOther},
+{dd3082,0,0,0,So,CD,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
+/* FFF0-FFFF */
+static const struct unidata st640[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -16944,873 +17187,745 @@ static const struct unidata st633[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 1D400-1D40F */
-static const struct unidata st634[] = {
-{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd498,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+/* 10000-1000F */
+static const struct unidata st641[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* 1D410-1D41F */
-static const struct unidata st635[] = {
-{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd508,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+/* 10030-1003F */
+static const struct unidata st642[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* 1D420-1D42F */
-static const struct unidata st636[] = {
-{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+/* 10100-1010F */
+static const struct unidata st643[] = {
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther}
 };
-/* 1D430-1D43F */
-static const struct unidata st637[] = {
-{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd498,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
-};
-/* 1D440-1D44F */
-static const struct unidata st638[] = {
-{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd508,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
-};
-/* 1D450-1D45F */
-static const struct unidata st639[] = {
-{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+/* 10130-1013F */
+static const struct unidata st644[] = {
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
-/* 1D460-1D46F */
-static const struct unidata st640[] = {
-{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd498,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+/* 10140-1014F */
+static const struct unidata st645[] = {
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter}
 };
-/* 1D470-1D47F */
-static const struct unidata st641[] = {
-{dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd508,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+/* 10170-1017F */
+static const struct unidata st646[] = {
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
-/* 1D480-1D48F */
-static const struct unidata st642[] = {
-{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+/* 10180-1018F */
+static const struct unidata st647[] = {
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 1D490-1D49F */
-static const struct unidata st643[] = {
-{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+/* 101F0-101FF */
+static const struct unidata st648[] = {
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 1D4A0-1D4AF */
-static const struct unidata st644[] = {
+/* 10310-1031F */
+static const struct unidata st649[] = {
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
+/* 10320-1032F */
+static const struct unidata st650[] = {
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
-};
-/* 1D4B0-1D4BF */
-static const struct unidata st645[] = {
-{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
-};
-/* 1D4C0-1D4CF */
-static const struct unidata st646[] = {
-{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
-};
-/* 1D500-1D50F */
-static const struct unidata st647[] = {
-{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
-};
-/* 1D510-1D51F */
-static const struct unidata st648[] = {
-{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
-};
-/* 1D520-1D52F */
-static const struct unidata st649[] = {
-{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
-};
-/* 1D530-1D53F */
-static const struct unidata st650[] = {
-{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 1D540-1D54F */
+/* 10340-1034F */
 static const struct unidata st651[] = {
-{dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 1D550-1D55F */
+/* 10390-1039F */
 static const struct unidata st652[] = {
-{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
-/* 1D560-1D56F */
+/* 103C0-103CF */
 static const struct unidata st653[] = {
-{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* 1D570-1D57F */
+/* 103D0-103DF */
 static const struct unidata st654[] = {
-{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd498,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd508,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 1D580-1D58F */
+/* 10400-1040F */
 static const struct unidata st655[] = {
-{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,dd4851,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4852,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4853,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4854,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4855,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4856,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4857,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4858,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4859,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4860,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4861,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4862,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4863,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4864,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4865,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4866,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
-/* 1D590-1D59F */
+/* 10410-1041F */
 static const struct unidata st656[] = {
-{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,dd4867,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4868,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4869,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4870,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4871,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4872,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4873,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4874,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4875,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4876,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4877,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4878,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4879,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4880,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4881,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4882,0,0,Lu,0,GBOther,WBALetter,SBUpper}
 };
-/* 1D6A0-1D6AF */
+/* 10420-1042F */
 static const struct unidata st657[] = {
-{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3095,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3096,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd3097,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3100,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3102,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd342,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{0,dd4883,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4884,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4885,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4886,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4887,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4888,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4889,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,dd4890,0,0,Lu,0,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower},
+{0,0,0,0,Ll,0,GBOther,WBALetter,SBLower}
 };
-/* 1D6B0-1D6BF */
+/* 10800-1080F */
 static const struct unidata st658[] = {
-{dd3103,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3104,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3105,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3106,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3109,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3110,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3111,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd344,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3112,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd334,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3113,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3114,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3115,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* 1D6C0-1D6CF */
+/* 10830-1083F */
 static const struct unidata st659[] = {
-{dd1099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3116,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3117,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd332,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd535,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd536,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd343,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3118,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3119,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd333,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd996,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd339,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3120,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd7,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3121,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3122,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* 1D6D0-1D6DF */
+/* 10910-1091F */
 static const struct unidata st660[] = {
-{dd3123,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd338,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd340,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd341,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3124,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3125,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3126,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd337,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd537,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3127,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3128,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3129,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3130,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3131,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3132,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3133,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
-/* 1D6E0-1D6EF */
+/* 10930-1093F */
 static const struct unidata st661[] = {
-{dd3134,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3135,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3097,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3100,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3102,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd342,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3103,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3104,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3105,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3106,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther}
 };
-/* 1D6F0-1D6FF */
+/* 10A00-10A0F */
 static const struct unidata st662[] = {
-{dd3109,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3110,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3111,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd344,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3112,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd334,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3113,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3114,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3115,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3116,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3117,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd332,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd535,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd536,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend}
 };
-/* 1D700-1D70F */
+/* 10A10-10A1F */
 static const struct unidata st663[] = {
-{dd343,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3118,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3119,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd333,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd996,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd339,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3120,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd7,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3121,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3122,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3123,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd338,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd340,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd341,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3124,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3125,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter}
 };
-/* 1D710-1D71F */
+/* 10A30-10A3F */
 static const struct unidata st664[] = {
-{dd3126,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd337,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd537,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3127,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3128,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3129,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3130,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3131,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3132,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3133,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3134,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3135,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3097,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,1,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,9,Mn,0,GBExtend,WBExtend,SBExtend}
 };
-/* 1D720-1D72F */
+/* 10A40-10A4F */
 static const struct unidata st665[] = {
-{dd3100,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3102,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd342,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3103,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3104,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3105,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3106,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3109,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3110,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3111,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd344,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3112,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 1D730-1D73F */
+/* 10A50-10A5F */
 static const struct unidata st666[] = {
-{dd334,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3113,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3114,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3115,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3116,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3117,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd332,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd535,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd536,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd343,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3118,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3119,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd333,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd996,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd339,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 1D740-1D74F */
+/* 12460-1246F */
 static const struct unidata st667[] = {
-{dd3120,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd7,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3121,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3122,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3123,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd338,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd340,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd341,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3124,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3125,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3126,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd337,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd537,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3127,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3128,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3129,0,0,0,Sm,CD,GBOther,WBOther,SBOther}
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Nl,0,GBOther,WBALetter,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 1D750-1D75F */
+/* 12470-1247F */
 static const struct unidata st668[] = {
-{dd3130,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3131,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3132,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3133,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3134,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3135,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3097,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3100,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3102,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd342,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3103,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3104,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Po,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 1D760-1D76F */
+/* 1D0F0-1D0FF */
 static const struct unidata st669[] = {
-{dd3105,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3106,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3109,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3110,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3111,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd344,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3112,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd334,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3113,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3114,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3115,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3116,0,0,0,Sm,CD,GBOther,WBOther,SBOther}
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 1D770-1D77F */
+/* 1D120-1D12F */
 static const struct unidata st670[] = {
-{dd3117,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd332,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd535,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd536,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd343,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3118,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3119,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd333,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd996,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd339,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3120,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd7,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3121,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3122,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3123,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd338,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
-/* 1D780-1D78F */
+/* 1D150-1D15F */
 static const struct unidata st671[] = {
-{dd340,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd341,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3124,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3125,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3126,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd337,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd537,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3127,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3128,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3129,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3130,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3131,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3132,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3133,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3134,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3135,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{dd3083,0,0,0,So,0,GBOther,WBOther,SBOther},
+{dd3084,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
-/* 1D790-1D79F */
+/* 1D160-1D16F */
 static const struct unidata st672[] = {
-{dd3097,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3100,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3102,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd342,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3103,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3104,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3105,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3106,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3109,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+{dd3085,0,0,0,So,0,GBOther,WBOther,SBOther},
+{dd3086,0,0,0,So,0,GBOther,WBOther,SBOther},
+{dd3087,0,0,0,So,0,GBOther,WBOther,SBOther},
+{dd3088,0,0,0,So,0,GBOther,WBOther,SBOther},
+{dd3089,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,216,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,1,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,1,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,1,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,226,Mc,0,GBSpacingMark,WBExtend,SBExtend},
+{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend}
 };
-/* 1D7A0-1D7AF */
+/* 1D170-1D17F */
 static const struct unidata st673[] = {
-{dd3110,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3111,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd344,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3112,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd334,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3113,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3114,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3115,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd1099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3116,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3117,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd332,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd535,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd536,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd343,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3118,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,216,Mc,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend}
 };
-/* 1D7B0-1D7BF */
+/* 1D180-1D18F */
 static const struct unidata st674[] = {
-{dd3119,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd333,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd996,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd339,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3120,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd7,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3121,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3122,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3123,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd338,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd340,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd341,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3124,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3125,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3126,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd337,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,220,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
-/* 1D7C0-1D7CF */
+/* 1D1A0-1D1AF */
 static const struct unidata st675[] = {
-{dd537,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3127,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3128,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3129,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
-{dd3130,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3131,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3132,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3133,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3134,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3135,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{dd3136,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
-{dd3137,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{dd1070,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd9,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric}
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
-/* 1D7D0-1D7DF */
+/* 1D1B0-1D1BF */
 static const struct unidata st676[] = {
-{dd4,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd5,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1071,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1072,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1073,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1074,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1075,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1076,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1070,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd9,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd4,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd5,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1071,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1072,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1073,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1074,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric}
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{dd3090,0,0,0,So,0,GBOther,WBOther,SBOther},
+{dd3091,0,0,0,So,0,GBOther,WBOther,SBOther},
+{dd3092,0,0,0,So,0,GBOther,WBOther,SBOther},
+{dd3093,0,0,0,So,0,GBOther,WBOther,SBOther},
+{dd3094,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
-/* 1D7E0-1D7EF */
+/* 1D1C0-1D1CF */
 static const struct unidata st677[] = {
-{dd1075,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1076,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1070,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd9,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd4,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd5,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1071,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1072,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1073,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1074,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1075,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1076,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1070,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd9,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd4,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd5,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric}
+{dd3095,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther}
 };
-/* 1D7F0-1D7FF */
+/* 1D240-1D24F */
 static const struct unidata st678[] = {
-{dd1071,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1072,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1073,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1074,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1075,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1076,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1070,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd9,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd4,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd5,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1071,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1072,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1073,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1074,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1075,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
-{dd1076,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric}
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,230,Mn,0,GBExtend,WBExtend,SBExtend},
+{0,0,0,0,So,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 2A6D0-2A6DF */
+/* 1D370-1D37F */
 static const struct unidata st679[] = {
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,No,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -17821,664 +17936,873 @@ static const struct unidata st679[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 2F800-2F80F */
+/* 1D400-1D40F */
 static const struct unidata st680[] = {
-{dd3138,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3139,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3140,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3141,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3142,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2399,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3143,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3144,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3145,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3146,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2400,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3147,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3148,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3149,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2401,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3150,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd498,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F810-2F81F */
+/* 1D410-1D41F */
 static const struct unidata st681[] = {
-{dd3151,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3152,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3153,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3154,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3155,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3156,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3157,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3158,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3159,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3160,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3161,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2453,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3162,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1297,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3163,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3164,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* 2F820-2F82F */
-static const struct unidata st682[] = {
-{dd3165,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3166,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3167,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3168,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3169,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2458,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2402,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2403,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2459,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3170,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3171,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2222,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3172,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2404,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3173,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3174,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd508,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F830-2F83F */
+/* 1D420-1D42F */
+static const struct unidata st682[] = {
+{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+};
+/* 1D430-1D43F */
 static const struct unidata st683[] = {
-{dd3175,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3176,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3176,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3176,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3177,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3178,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3179,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3180,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3181,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3182,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3183,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3184,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3185,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3186,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3187,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3188,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd498,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F840-2F84F */
+/* 1D440-1D44F */
 static const struct unidata st684[] = {
-{dd3189,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3190,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3191,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3192,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3193,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3194,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3194,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2461,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3195,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3196,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3197,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3198,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2406,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3199,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3200,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3201,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd508,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F850-2F85F */
+/* 1D450-1D45F */
 static const struct unidata st685[] = {
-{dd2368,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3202,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3203,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3204,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3205,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3206,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3207,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3208,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3209,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3210,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3211,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3212,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3213,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3214,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3215,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3216,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F860-2F86F */
+/* 1D460-1D46F */
 static const struct unidata st686[] = {
-{dd3217,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3218,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3219,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3220,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3221,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3222,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3223,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3224,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3225,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3226,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3227,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3227,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3228,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3229,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3230,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2218,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd498,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F870-2F87F */
+/* 1D470-1D47F */
 static const struct unidata st687[] = {
-{dd3231,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3232,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3233,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3234,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3235,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1323,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3236,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3237,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1325,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3238,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3239,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3240,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3241,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3242,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3243,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3244,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd508,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F880-2F88F */
+/* 1D480-1D48F */
 static const struct unidata st688[] = {
-{dd3245,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3246,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3247,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3248,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3249,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3250,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3251,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3252,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3253,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3254,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3255,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3256,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3257,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3258,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2166,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3259,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F890-2F89F */
+/* 1D490-1D49F */
 static const struct unidata st689[] = {
-{dd1335,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3260,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3260,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3261,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3262,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3262,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3263,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3264,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3265,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3266,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3267,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3268,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3269,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3270,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3271,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3272,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* 2F8A0-2F8AF */
-static const struct unidata st690[] = {
-{dd3273,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3274,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3275,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2411,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3276,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3277,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3278,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3279,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2473,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3279,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3280,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2413,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3281,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3282,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3283,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3284,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F8B0-2F8BF */
+/* 1D4A0-1D4AF */
+static const struct unidata st690[] = {
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+};
+/* 1D4B0-1D4BF */
 static const struct unidata st691[] = {
-{dd2414,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2139,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3285,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3286,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3287,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3288,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3289,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3290,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3291,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3292,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3293,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3294,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3295,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3296,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3297,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3298,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F8C0-2F8CF */
+/* 1D4C0-1D4CF */
 static const struct unidata st692[] = {
-{dd3299,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3300,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3301,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3302,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3303,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3304,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3305,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3306,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2415,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3307,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3308,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3309,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3310,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3311,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3312,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2417,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F8D0-2F8DF */
+/* 1D500-1D50F */
 static const struct unidata st693[] = {
-{dd3313,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3314,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3315,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3316,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3317,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3318,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3319,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3320,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2167,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2481,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3321,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3322,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3323,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3324,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3325,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3326,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F8E0-2F8EF */
+/* 1D510-1D51F */
 static const struct unidata st694[] = {
-{dd3327,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3328,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2418,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3329,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3330,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3331,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3332,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2523,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3333,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3334,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3335,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3336,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3337,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3338,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3339,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3340,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F8F0-2F8FF */
+/* 1D520-1D52F */
 static const struct unidata st695[] = {
-{dd3341,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3342,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3343,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3344,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3345,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2235,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3346,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3347,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3348,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3349,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3350,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3351,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3352,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3353,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3354,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3355,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F900-2F90F */
+/* 1D530-1D53F */
 static const struct unidata st696[] = {
-{dd3356,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2419,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2318,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3357,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3358,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3359,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3360,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3361,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3362,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3363,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3364,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2484,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3365,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3366,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3367,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3368,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* 2F910-2F91F */
+/* 1D540-1D54F */
 static const struct unidata st697[] = {
-{dd3369,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3370,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3371,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3372,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2485,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3373,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3374,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3375,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3376,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3377,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3378,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3379,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3380,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3381,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3382,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3383,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* 2F920-2F92F */
-static const struct unidata st698[] = {
-{dd3384,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2487,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3385,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3386,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3387,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3388,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3389,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3390,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3391,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3392,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3393,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3394,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3395,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3395,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3396,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3397,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F930-2F93F */
+/* 1D550-1D55F */
+static const struct unidata st698[] = {
+{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+};
+/* 1D560-1D56F */
 static const struct unidata st699[] = {
-{dd2489,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3398,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3399,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3400,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3401,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3402,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3403,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3404,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2221,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3405,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3406,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3407,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3408,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3409,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3410,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3411,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd491,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd493,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1086,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd494,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F940-2F94F */
+/* 1D570-1D57F */
 static const struct unidata st700[] = {
-{dd2495,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3412,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3413,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3414,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3415,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3416,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3417,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3417,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2496,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2525,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3418,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3419,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3420,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3421,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3422,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2184,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd495,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd497,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd498,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd499,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd500,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd501,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd502,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd503,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd504,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd505,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd507,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1094,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd508,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1270,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd509,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F950-2F95F */
+/* 1D580-1D58F */
 static const struct unidata st701[] = {
-{dd2498,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3423,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3424,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2429,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3425,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3426,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2388,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3427,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3428,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2432,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3429,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3430,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3431,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3432,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3432,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3433,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd510,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1126,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd511,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1131,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1271,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd2,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd515,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd541,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd516,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd517,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd544,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd521,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd284,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd538,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd286,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F960-2F96F */
+/* 1D590-1D59F */
 static const struct unidata st702[] = {
-{dd3434,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3435,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3436,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3437,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3438,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3439,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3440,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3441,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3442,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3443,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3444,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3445,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3446,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3447,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3448,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3449,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd522,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd300,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd523,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1082,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd10,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd528,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd1272,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd287,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd180,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd529,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd530,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd533,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F970-2F97F */
+/* 1D6A0-1D6AF */
 static const struct unidata st703[] = {
-{dd3450,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3451,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3452,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3453,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3454,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3455,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3456,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3457,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3458,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3459,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2438,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3460,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3461,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3462,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3463,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3464,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd291,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd301,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd292,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd571,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3096,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3097,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd3098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3100,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3102,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3103,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd342,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F980-2F98F */
+/* 1D6B0-1D6BF */
 static const struct unidata st704[] = {
-{dd3465,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3466,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3467,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3468,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3469,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3470,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3471,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3472,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3473,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3474,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3475,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3261,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3476,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3477,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3478,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3479,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd3104,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3105,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3106,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3109,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3110,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3111,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3112,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd344,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3113,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd334,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3114,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3115,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3116,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F990-2F99F */
+/* 1D6C0-1D6CF */
 static const struct unidata st705[] = {
-{dd3480,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3481,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3482,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3483,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3484,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3485,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3486,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3487,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2238,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3488,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3489,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3490,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3491,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3492,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3493,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2441,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
-};
-/* 2F9A0-2F9AF */
+{dd1099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3117,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3118,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd332,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd535,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd536,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd343,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3119,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3120,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd333,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd996,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd339,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3121,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd7,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3122,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3123,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+};
+/* 1D6D0-1D6DF */
 static const struct unidata st706[] = {
-{dd3494,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3495,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3496,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3497,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3498,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3499,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3500,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3501,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3502,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3503,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3504,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3505,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3506,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3507,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3508,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3509,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd3124,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd338,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd340,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd341,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3125,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3126,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3127,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd337,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd537,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3128,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3129,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3130,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3131,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3132,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3133,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3134,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F9B0-2F9BF */
+/* 1D6E0-1D6EF */
 static const struct unidata st707[] = {
-{dd3510,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3511,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3512,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3513,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2179,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3514,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3515,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3516,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3517,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3518,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3519,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2505,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3520,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3521,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3522,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3523,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd3135,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3136,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3100,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3102,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3103,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd342,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3104,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3105,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3106,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3109,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F9C0-2F9CF */
+/* 1D6F0-1D6FF */
 static const struct unidata st708[] = {
-{dd3524,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3525,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3526,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3527,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1425,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3528,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3529,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3530,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3531,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3532,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3533,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3534,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3535,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3536,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3537,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3538,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd3110,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3111,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3112,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd344,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3113,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd334,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3114,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3115,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3116,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3117,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3118,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd332,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd535,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd536,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F9D0-2F9DF */
+/* 1D700-1D70F */
 static const struct unidata st709[] = {
-{dd2510,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2511,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1432,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3539,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3540,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3541,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3542,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3543,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3544,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3545,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3546,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3547,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3548,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3549,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3550,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2512,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd343,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3119,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3120,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd333,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd996,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd339,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3121,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd7,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3122,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3123,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3124,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd338,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd340,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd341,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3125,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3126,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2F9E0-2F9EF */
+/* 1D710-1D71F */
 static const struct unidata st710[] = {
-{dd3551,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3552,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3553,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3554,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3555,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3556,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3557,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3558,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3559,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3560,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3561,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3562,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3563,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3564,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3565,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3566,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd3127,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd337,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd537,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3128,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3129,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3130,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3131,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3132,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3133,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3134,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3135,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3136,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3100,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2F9F0-2F9FF */
+/* 1D720-1D72F */
 static const struct unidata st711[] = {
-{dd3567,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3568,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3569,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3570,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3571,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3572,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3573,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3574,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3575,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3576,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3577,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3578,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3579,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3580,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2518,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2518,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd3101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3102,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3103,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd342,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3104,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3105,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3106,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3109,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3110,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3111,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3112,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd344,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3113,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
 };
-/* 2FA00-2FA0F */
+/* 1D730-1D73F */
 static const struct unidata st712[] = {
-{dd3581,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3582,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3583,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3584,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3585,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3586,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3587,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3588,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3589,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3590,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd2519,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3591,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3592,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3593,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3594,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3595,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+{dd334,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3114,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3115,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3116,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3117,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3118,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd332,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd535,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd536,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd343,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3119,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3120,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd333,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd996,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd339,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
 };
-/* 2FA10-2FA1F */
+/* 1D740-1D74F */
 static const struct unidata st713[] = {
-{dd3596,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3597,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3598,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3599,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3600,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1480,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3601,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1484,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3602,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3603,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3604,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3605,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd1489,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{dd3606,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
-};
-/* E0000-E000F */
-static const struct unidata st714[] = {
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
-{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd3121,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd7,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3122,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3123,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3124,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd338,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd340,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd341,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3125,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3126,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3127,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd337,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd537,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3128,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3129,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3130,0,0,0,Sm,CD,GBOther,WBOther,SBOther}
+};
+/* 1D750-1D75F */
+static const struct unidata st714[] = {
+{dd3131,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3132,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3133,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3134,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3135,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3136,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3100,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3102,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3103,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd342,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3104,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3105,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+};
+/* 1D760-1D76F */
+static const struct unidata st715[] = {
+{dd3106,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3109,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3110,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3111,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3112,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd344,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3113,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd334,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3114,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3115,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3116,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3117,0,0,0,Sm,CD,GBOther,WBOther,SBOther}
+};
+/* 1D770-1D77F */
+static const struct unidata st716[] = {
+{dd3118,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd332,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd535,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd536,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd343,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3119,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3120,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd333,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd996,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd339,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3121,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd7,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3122,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3123,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3124,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd338,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+};
+/* 1D780-1D78F */
+static const struct unidata st717[] = {
+{dd340,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd341,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3125,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3126,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3127,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd337,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd537,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3128,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3129,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3130,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3131,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3132,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3133,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3134,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3135,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3136,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+};
+/* 1D790-1D79F */
+static const struct unidata st718[] = {
+{dd3098,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3100,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3101,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3102,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3103,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd342,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3104,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3105,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3106,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3107,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3109,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3110,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1108,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper}
+};
+/* 1D7A0-1D7AF */
+static const struct unidata st719[] = {
+{dd3111,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3112,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd344,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3113,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd334,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3114,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3115,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3116,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd1099,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3117,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3118,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd332,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd535,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd536,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd343,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3119,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+};
+/* 1D7B0-1D7BF */
+static const struct unidata st720[] = {
+{dd3120,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd333,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd996,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd339,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3121,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd7,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3122,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3123,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3124,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd338,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd340,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd341,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3125,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3126,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3127,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd337,0,0,0,Ll,CD,GBOther,WBALetter,SBLower}
+};
+/* 1D7C0-1D7CF */
+static const struct unidata st721[] = {
+{dd537,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3128,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3129,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3130,0,0,0,Sm,CD,GBOther,WBOther,SBOther},
+{dd3131,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3132,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3133,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3134,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3135,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3136,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{dd3137,0,0,0,Lu,CD,GBOther,WBALetter,SBUpper},
+{dd3138,0,0,0,Ll,CD,GBOther,WBALetter,SBLower},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{dd1070,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd9,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric}
+};
+/* 1D7D0-1D7DF */
+static const struct unidata st722[] = {
+{dd4,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd5,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1071,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1072,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1073,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1074,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1075,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1076,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1070,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd9,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd4,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd5,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1071,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1072,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1073,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1074,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric}
+};
+/* 1D7E0-1D7EF */
+static const struct unidata st723[] = {
+{dd1075,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1076,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1070,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd9,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd4,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd5,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1071,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1072,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1073,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1074,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1075,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1076,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1070,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd9,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd4,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd5,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric}
+};
+/* 1D7F0-1D7FF */
+static const struct unidata st724[] = {
+{dd1071,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1072,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1073,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1074,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1075,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1076,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1070,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd9,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd4,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd5,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1071,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1072,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1073,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1074,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1075,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric},
+{dd1076,0,0,0,Nd,CD,GBOther,WBNumeric,SBNumeric}
+};
+/* 2A6D0-2A6DF */
+static const struct unidata st725[] = {
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
@@ -18486,12186 +18810,8179 @@ static const struct unidata st714[] = {
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
 {0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
 };
-/* E0020-E002F */
-static const struct unidata st715[] = {
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
-{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat}
+/* 2F800-2F80F */
+static const struct unidata st726[] = {
+{dd3139,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3140,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3141,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3142,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3143,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2400,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3144,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3145,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3146,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3147,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2401,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3148,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3149,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3150,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2402,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3151,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
 };
-const struct unidata *const unidata[]={
-st0,
-st1,
-st2,
-st3,
-st4,
-st5,
-st6,
-st7,
-st8,
-st1,
-st9,
-st10,
-st11,
-st12,
-st13,
-st14,
-st15,
-st16,
-st17,
-st18,
-st19,
-st20,
-st21,
-st22,
-st23,
-st24,
-st25,
-st26,
-st27,
-st28,
-st29,
-st30,
-st31,
-st32,
-st33,
-st34,
-st35,
-st36,
-st36,
-st36,
-st36,
-st37,
-st36,
-st38,
-st39,
-st40,
-st41,
-st42,
-st43,
-st44,
-st45,
-st46,
-st47,
-st48,
-st49,
-st50,
-st51,
-st52,
-st53,
-st54,
-st55,
-st56,
-st57,
-st58,
-st59,
-st60,
-st61,
-st62,
-st63,
-st64,
-st65,
-st66,
-st67,
-st68,
-st69,
-st70,
-st71,
-st72,
-st73,
-st74,
-st75,
-st76,
-st77,
-st78,
-st79,
-st80,
-st81,
-st36,
-st82,
-st83,
-st84,
-st85,
-st86,
-st87,
-st88,
-st89,
-st90,
-st91,
-st92,
-st88,
-st93,
-st94,
-st95,
-st96,
-st87,
-st87,
-st87,
-st87,
-st97,
-st98,
-st99,
-st100,
-st101,
-st102,
-st87,
-st103,
-st104,
-st87,
-st105,
-st77,
-st87,
-st87,
-st106,
-st107,
-st108,
-st87,
-st109,
-st110,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st111,
-st87,
-st112,
-st113,
-st114,
-st115,
-st116,
-st117,
-st118,
-st119,
-st120,
-st121,
-st122,
-st123,
-st124,
-st125,
-st126,
-st119,
-st120,
-st127,
-st128,
-st129,
-st130,
-st131,
-st132,
-st133,
-st120,
-st134,
-st135,
-st136,
-st124,
-st137,
-st118,
-st119,
-st120,
-st138,
-st139,
-st140,
-st141,
-st142,
-st143,
-st144,
-st145,
-st146,
-st147,
-st148,
-st130,
-st149,
-st150,
-st151,
-st120,
-st152,
-st153,
-st154,
-st141,
-st77,
-st155,
-st151,
-st120,
-st156,
-st157,
-st158,
-st124,
-st159,
-st155,
-st151,
-st120,
-st146,
-st160,
-st148,
-st141,
-st77,
-st161,
-st162,
-st87,
-st163,
-st164,
-st165,
-st77,
-st166,
-st167,
-st168,
-st168,
-st169,
-st170,
-st171,
-st77,
-st77,
-st172,
-st173,
-st174,
-st175,
-st176,
-st177,
-st77,
-st77,
-st178,
-st179,
-st180,
-st181,
-st182,
-st183,
-st184,
-st185,
-st186,
-st187,
-st188,
-st189,
-st190,
-st191,
-st77,
-st77,
-st168,
-st168,
-st192,
-st193,
-st194,
-st195,
-st77,
-st77,
-st77,
-st77,
-st196,
-st197,
-st198,
-st87,
-st87,
-st199,
-st200,
-st200,
-st200,
-st200,
-st200,
-st201,
-st202,
-st202,
-st202,
-st202,
-st203,
-st204,
-st204,
-st204,
-st204,
-st205,
-st87,
-st87,
-st87,
-st87,
-st206,
-st207,
-st87,
-st87,
-st206,
-st87,
-st87,
-st208,
-st209,
-st210,
-st87,
-st87,
-st87,
-st209,
-st87,
-st87,
-st87,
-st211,
-st212,
-st213,
-st87,
-st214,
-st87,
-st87,
-st87,
-st87,
-st87,
-st215,
-st216,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st217,
-st218,
-st219,
-st220,
-st87,
-st87,
-st87,
-st87,
-st221,
-st222,
-st223,
-st224,
-st87,
-st225,
-st87,
-st226,
-st223,
-st227,
-st168,
-st168,
-st168,
-st228,
-st229,
-st230,
-st231,
-st232,
-st233,
-st231,
-st87,
-st87,
-st234,
-st87,
-st87,
-st235,
-st87,
-st87,
-st236,
-st77,
-st77,
-st77,
-st77,
-st77,
-st87,
-st237,
-st238,
-st239,
-st240,
-st168,
-st241,
-st242,
-st168,
-st168,
-st243,
-st244,
-st245,
-st246,
-st247,
-st247,
-st87,
-st248,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st249,
-st250,
-st87,
-st251,
-st252,
-st253,
-st254,
-st255,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st36,
-st36,
-st256,
-st257,
-st258,
-st259,
-st260,
-st261,
-st36,
-st262,
-st263,
-st264,
-st265,
-st77,
-st77,
-st266,
-st267,
-st268,
-st269,
-st270,
-st271,
-st272,
-st273,
-st274,
-st275,
-st276,
-st277,
-st278,
-st279,
-st280,
-st281,
-st282,
-st283,
-st284,
-st285,
-st286,
-st287,
-st288,
-st289,
-st290,
-st291,
-st292,
-st293,
-st294,
-st295,
-st296,
-st297,
-st298,
-st299,
-st300,
-st301,
-st302,
-st303,
-st304,
-st305,
-st306,
-st307,
-st308,
-st309,
-st310,
-st77,
-st311,
-st312,
-st77,
-st313,
-st314,
-st315,
-st316,
-st317,
-st318,
-st319,
-st320,
-st321,
-st322,
-st323,
-st247,
-st324,
-st325,
-st247,
-st326,
-st327,
-st328,
-st329,
-st330,
-st331,
-st328,
-st332,
-st333,
-st334,
-st335,
-st336,
-st337,
-st328,
-st328,
-st338,
-st328,
-st339,
-st247,
-st340,
-st247,
-st247,
-st247,
-st247,
-st341,
-st247,
-st342,
-st328,
-st343,
-st247,
-st344,
-st345,
-st77,
-st247,
-st247,
-st346,
-st77,
-st347,
-st77,
-st348,
-st349,
-st350,
-st351,
-st352,
-st353,
-st354,
-st355,
-st356,
-st357,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st358,
-st359,
-st247,
-st247,
-st360,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st361,
-st247,
-st247,
-st362,
-st247,
-st363,
-st77,
-st77,
-st77,
-st77,
-st364,
-st247,
-st365,
-st247,
-st366,
-st367,
-st368,
-st369,
-st357,
-st370,
-st247,
-st371,
-st372,
-st328,
-st373,
-st328,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st328,
-st328,
-st328,
-st328,
-st328,
-st328,
-st328,
-st328,
-st374,
-st375,
-st328,
-st328,
-st328,
-st376,
-st328,
-st377,
-st378,
-st328,
-st328,
-st328,
-st328,
-st328,
-st328,
-st379,
-st328,
-st328,
-st328,
-st328,
-st328,
-st380,
-st328,
-st328,
-st247,
-st347,
-st381,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st382,
-st383,
-st384,
-st36,
-st36,
-st385,
-st386,
-st387,
-st388,
-st389,
-st390,
-st391,
-st392,
-st393,
-st394,
-st395,
-st36,
-st36,
-st396,
-st87,
-st87,
-st87,
-st397,
-st77,
-st87,
-st218,
-st398,
-st398,
-st398,
-st398,
-st77,
-st77,
-st399,
-st400,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st247,
-st401,
-st247,
-st247,
-st247,
-st247,
-st247,
-st402,
-st403,
-st404,
-st405,
-st406,
-st407,
-st408,
-st409,
-st410,
-st411,
-st412,
-st413,
-st414,
-st415,
-st416,
-st77,
-st417,
-st418,
-st419,
-st420,
-st421,
-st422,
-st423,
-st424,
-st425,
-st168,
-st426,
-st427,
-st428,
-st429,
-st430,
-st431,
-st432,
-st433,
-st87,
-st237,
-st434,
-st435,
-st436,
-st437,
-st438,
-st439,
-st440,
-st87,
-st235,
-st247,
-st77,
-st77,
-st441,
-st442,
-st443,
-st444,
-st445,
-st446,
-st447,
-st448,
-st449,
-st450,
-st451,
-st452,
-st453,
-st454,
-st455,
-st456,
-st457,
-st458,
-st459,
-st460,
-st461,
-st462,
-st463,
-st464,
-st465,
-st466,
-st467,
-st468,
-st469,
-st470,
-st471,
-st472,
-st473,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st474,
-st247,
-st247,
-st247,
-st247,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st475,
-st77,
-st77,
-st77,
-st77,
-st87,
-st476,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st237,
-st247,
-st247,
-st247,
-st346,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st42,
-st477,
-st478,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st479,
-st87,
-st480,
-st77,
-st87,
-st87,
-st87,
-st481,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st486,
-st484,
-st482,
-st483,
-st484,
-st485,
-st484,
-st487,
-st77,
-st77,
-st77,
-st77,
-st77,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st488,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st489,
-st490,
-st491,
-st492,
-st493,
-st494,
-st495,
-st496,
-st497,
-st498,
-st499,
-st500,
-st501,
-st502,
-st503,
-st504,
-st505,
-st506,
-st507,
-st508,
-st509,
-st510,
-st511,
-st512,
-st513,
-st514,
-st515,
-st516,
-st517,
-st518,
-st519,
-st77,
-st77,
-st520,
-st521,
-st522,
-st523,
-st524,
-st525,
-st526,
-st527,
-st528,
-st529,
-st530,
-st531,
-st77,
-st532,
-st533,
-st534,
-st535,
-st536,
-st537,
-st538,
-st539,
-st540,
-st541,
-st542,
-st543,
-st544,
-st545,
-st546,
-st547,
-st548,
-st549,
-st550,
-st551,
-st552,
-st553,
-st554,
-st77,
-st555,
-st556,
-st557,
-st558,
-st559,
-st560,
-st561,
-st562,
-st77,
-st77,
-st563,
-st564,
-st565,
-st566,
-st567,
-st568,
-st569,
-st570,
-st571,
-st572,
-st573,
-st574,
-st575,
-st576,
-st577,
-st578,
-st579,
-st580,
-st581,
-st582,
-st583,
-st584,
-st585,
-st586,
-st587,
-st588,
-st589,
-st590,
-st591,
-st592,
-st593,
-st594,
-st595,
-st596,
-st87,
-st210,
-st597,
-st105,
-st105,
-st77,
-st77,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st88,
-st598,
-st357,
-st357,
-st599,
-st600,
-st600,
-st600,
-st601,
-st602,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st87,
-st603,
-st604,
-st87,
-st605,
-st77,
-st77,
-st77,
-st87,
-st606,
-st87,
-st87,
-st607,
-st608,
-st77,
-st77,
-st609,
-st610,
-st611,
-st36,
-st36,
-st87,
-st87,
-st87,
-st87,
-st105,
-st231,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st612,
-st87,
-st87,
-st613,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st87,
-st614,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st615,
-st616,
-st87,
-st617,
-st618,
-st619,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st87,
-st603,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st600,
-st600,
-st600,
-st600,
-st600,
-st600,
-st620,
-st621,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st247,
-st622,
-st247,
-st247,
-st623,
-st247,
-st247,
-st624,
-st625,
-st626,
-st627,
-st247,
-st628,
-st629,
-st630,
-st631,
-st77,
-st77,
-st247,
-st247,
-st247,
-st247,
-st632,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st247,
-st247,
-st247,
-st247,
-st247,
-st346,
-st357,
-st633,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st634,
-st635,
-st636,
-st637,
-st638,
-st639,
-st640,
-st641,
-st642,
-st643,
-st644,
-st645,
-st646,
-st634,
-st635,
-st636,
-st647,
-st648,
-st649,
-st650,
-st651,
-st652,
-st653,
-st654,
-st655,
-st656,
-st634,
-st635,
-st636,
-st637,
-st638,
-st649,
-st640,
-st641,
-st642,
-st653,
-st654,
-st655,
-st656,
-st634,
-st635,
-st636,
-st657,
-st658,
-st659,
-st660,
-st661,
-st662,
-st663,
-st664,
-st665,
-st666,
-st667,
-st668,
-st669,
-st670,
-st671,
-st672,
-st673,
-st674,
-st675,
-st676,
-st677,
-st678,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st168,
-st679,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
+/* 2F810-2F81F */
+static const struct unidata st727[] = {
+{dd3152,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3153,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3154,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3155,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3156,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3157,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3158,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3159,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3160,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3161,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3162,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2454,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3163,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1297,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3164,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3165,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F820-2F82F */
+static const struct unidata st728[] = {
+{dd3166,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3167,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3168,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3169,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3170,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2459,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2403,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2404,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2460,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3171,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3172,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2223,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3173,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2405,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3174,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3175,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F830-2F83F */
+static const struct unidata st729[] = {
+{dd3176,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3177,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3177,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3177,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3178,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3179,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3180,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3181,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3182,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3183,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3184,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3185,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3186,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3187,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3188,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3189,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F840-2F84F */
+static const struct unidata st730[] = {
+{dd3190,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3191,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3192,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3193,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3194,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3195,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3195,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2462,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3196,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3197,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3198,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3199,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2407,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3200,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3201,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3202,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F850-2F85F */
+static const struct unidata st731[] = {
+{dd2369,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3203,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3204,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3205,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3206,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3207,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3208,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3209,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3210,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3211,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3212,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3213,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3214,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3215,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3216,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3217,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F860-2F86F */
+static const struct unidata st732[] = {
+{dd3218,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3219,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3220,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3221,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3222,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3223,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3224,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3225,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3226,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3227,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3228,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3228,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3229,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3230,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3231,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2219,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F870-2F87F */
+static const struct unidata st733[] = {
+{dd3232,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3233,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3234,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3235,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3236,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1323,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3237,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3238,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1325,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3239,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3240,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3241,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3242,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3243,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3244,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3245,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F880-2F88F */
+static const struct unidata st734[] = {
+{dd3246,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3247,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3248,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3249,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3250,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3251,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3252,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3253,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3254,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3255,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3256,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3257,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3258,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3259,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2167,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3260,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F890-2F89F */
+static const struct unidata st735[] = {
+{dd1335,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3261,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3261,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3262,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3263,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3263,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3264,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3265,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3266,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3267,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3268,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3269,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3270,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3271,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3272,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3273,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F8A0-2F8AF */
+static const struct unidata st736[] = {
+{dd3274,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3275,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3276,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2412,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3277,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3278,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3279,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3280,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2474,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3280,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3281,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2414,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3282,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3283,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3284,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3285,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F8B0-2F8BF */
+static const struct unidata st737[] = {
+{dd2415,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2140,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3286,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3287,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3288,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3289,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3290,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3291,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3292,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3293,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3294,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3295,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3296,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3297,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3298,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3299,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F8C0-2F8CF */
+static const struct unidata st738[] = {
+{dd3300,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3301,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3302,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3303,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3304,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3305,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3306,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3307,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2416,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3308,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3309,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3310,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3311,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3312,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3313,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2418,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F8D0-2F8DF */
+static const struct unidata st739[] = {
+{dd3314,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3315,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3316,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3317,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3318,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3319,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3320,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3321,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2168,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2482,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3322,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3323,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3324,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3325,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3326,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3327,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F8E0-2F8EF */
+static const struct unidata st740[] = {
+{dd3328,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3329,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2419,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3330,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3331,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3332,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3333,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2524,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3334,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3335,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3336,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3337,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3338,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3339,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3340,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3341,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F8F0-2F8FF */
+static const struct unidata st741[] = {
+{dd3342,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3343,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3344,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3345,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3346,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2236,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3347,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3348,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3349,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3350,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3351,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3352,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3353,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3354,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3355,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3356,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F900-2F90F */
+static const struct unidata st742[] = {
+{dd3357,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2420,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2319,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3358,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3359,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3360,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3361,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3362,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3363,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3364,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3365,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2485,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3366,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3367,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3368,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3369,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F910-2F91F */
+static const struct unidata st743[] = {
+{dd3370,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3371,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3372,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3373,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2486,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3374,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3375,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3376,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3377,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3378,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3379,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3380,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3381,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3382,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3383,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3384,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F920-2F92F */
+static const struct unidata st744[] = {
+{dd3385,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2488,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3386,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3387,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3388,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3389,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3390,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3391,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3392,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3393,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3394,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3395,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3396,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3396,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3397,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3398,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F930-2F93F */
+static const struct unidata st745[] = {
+{dd2490,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3399,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3400,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3401,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3402,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3403,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3404,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3405,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2222,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3406,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3407,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3408,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3409,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3410,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3411,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3412,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F940-2F94F */
+static const struct unidata st746[] = {
+{dd2496,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3413,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3414,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3415,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3416,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3417,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3418,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3418,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2497,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2526,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3419,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3420,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3421,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3422,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3423,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2185,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F950-2F95F */
+static const struct unidata st747[] = {
+{dd2499,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3424,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3425,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2430,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3426,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3427,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2389,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3428,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3429,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2433,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3430,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3431,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3432,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3433,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3433,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3434,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F960-2F96F */
+static const struct unidata st748[] = {
+{dd3435,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3436,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3437,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3438,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3439,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3440,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3441,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3442,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3443,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3444,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3445,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3446,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3447,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3448,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3449,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3450,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F970-2F97F */
+static const struct unidata st749[] = {
+{dd3451,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3452,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3453,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3454,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3455,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3456,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3457,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3458,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3459,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3460,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2439,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3461,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3462,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3463,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3464,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3465,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F980-2F98F */
+static const struct unidata st750[] = {
+{dd3466,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3467,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3468,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3469,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3470,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3471,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3472,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3473,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3474,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3475,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3476,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3262,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3477,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3478,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3479,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3480,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F990-2F99F */
+static const struct unidata st751[] = {
+{dd3481,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3482,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3483,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3484,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3485,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3486,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3487,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3488,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2239,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3489,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3490,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3491,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3492,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3493,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3494,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2442,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F9A0-2F9AF */
+static const struct unidata st752[] = {
+{dd3495,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3496,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3497,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3498,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3499,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3500,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3501,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3502,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3503,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3504,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3505,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3506,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3507,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3508,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3509,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3510,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F9B0-2F9BF */
+static const struct unidata st753[] = {
+{dd3511,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3512,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3513,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3514,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2180,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3515,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3516,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3517,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3518,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3519,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3520,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2506,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3521,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3522,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3523,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3524,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F9C0-2F9CF */
+static const struct unidata st754[] = {
+{dd3525,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3526,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3527,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3528,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1425,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3529,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3530,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3531,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3532,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3533,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3534,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3535,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3536,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3537,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3538,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3539,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F9D0-2F9DF */
+static const struct unidata st755[] = {
+{dd2511,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2512,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1432,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3540,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3541,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3542,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3543,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3544,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3545,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3546,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3547,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3548,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3549,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3550,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3551,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2513,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F9E0-2F9EF */
+static const struct unidata st756[] = {
+{dd3552,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3553,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3554,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3555,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3556,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3557,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3558,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3559,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3560,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3561,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3562,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3563,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3564,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3565,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3566,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3567,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2F9F0-2F9FF */
+static const struct unidata st757[] = {
+{dd3568,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3569,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3570,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3571,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3572,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3573,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3574,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3575,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3576,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3577,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3578,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3579,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3580,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3581,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2519,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2519,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2FA00-2FA0F */
+static const struct unidata st758[] = {
+{dd3582,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3583,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3584,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3585,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3586,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3587,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3588,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3589,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3590,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3591,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd2520,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3592,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3593,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3594,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3595,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3596,0,0,0,Lo,0,GBOther,WBOther,SBOLetter}
+};
+/* 2FA10-2FA1F */
+static const struct unidata st759[] = {
+{dd3597,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3598,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3599,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3600,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3601,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1480,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3602,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1484,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3603,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3604,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3605,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3606,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd1489,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{dd3607,0,0,0,Lo,0,GBOther,WBOther,SBOLetter},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
+/* E0000-E000F */
+static const struct unidata st760[] = {
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther},
+{0,0,0,0,Cn,0,GBOther,WBOther,SBOther}
+};
+/* E0020-E002F */
+static const struct unidata st761[] = {
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat},
+{0,0,0,0,Cf,0,GBControl,WBFormat,SBFormat}
+};
+const struct unidata *const unidata[]={
+st0,
+st1,
+st2,
+st3,
+st4,
+st5,
+st6,
+st7,
+st8,
+st1,
+st9,
+st10,
+st11,
+st12,
+st13,
+st14,
+st15,
+st16,
+st17,
+st18,
+st19,
+st20,
+st21,
+st22,
+st23,
+st24,
+st25,
+st26,
+st27,
+st28,
+st29,
+st30,
+st31,
+st32,
+st33,
+st34,
+st35,
+st36,
+st36,
+st36,
+st36,
+st37,
+st36,
+st38,
+st39,
+st40,
+st41,
+st42,
+st43,
+st44,
+st45,
+st46,
+st47,
+st48,
+st49,
+st50,
+st51,
+st52,
+st53,
+st54,
+st55,
+st56,
+st57,
+st58,
+st59,
+st60,
+st61,
+st62,
+st63,
+st64,
+st65,
+st66,
+st67,
+st68,
+st69,
+st70,
+st71,
+st72,
+st73,
+st74,
+st75,
+st76,
 st77,
+st78,
+st79,
+st80,
+st81,
+st36,
+st82,
+st83,
+st84,
+st85,
+st86,
+st87,
+st88,
+st89,
+st90,
+st91,
+st92,
+st87,
+st93,
+st94,
+st95,
+st96,
+st87,
+st87,
+st87,
+st87,
+st97,
+st98,
+st99,
+st100,
+st101,
+st102,
+st87,
+st103,
+st104,
+st87,
+st87,
+st87,
+st87,
+st87,
+st105,
+st106,
+st107,
+st87,
+st108,
+st109,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st111,
+st87,
+st112,
+st113,
+st114,
+st115,
+st116,
+st117,
+st118,
+st119,
+st120,
+st121,
+st122,
+st123,
+st124,
+st125,
+st126,
+st119,
+st120,
+st127,
+st128,
+st129,
+st130,
+st131,
+st132,
+st133,
+st120,
+st134,
+st135,
+st136,
+st124,
+st137,
+st118,
+st119,
+st120,
+st138,
+st139,
+st140,
+st124,
+st141,
+st142,
+st143,
+st144,
+st145,
+st146,
+st147,
+st130,
+st148,
+st149,
+st150,
+st120,
+st151,
+st152,
+st153,
+st124,
+st154,
+st155,
+st150,
+st120,
+st156,
+st157,
+st158,
+st124,
+st159,
+st155,
+st150,
+st120,
+st160,
+st161,
+st162,
+st124,
+st163,
+st164,
+st165,
+st87,
+st166,
+st167,
+st168,
+st110,
+st169,
+st170,
+st171,
+st171,
+st172,
+st173,
+st174,
+st110,
+st110,
+st175,
+st176,
+st177,
+st178,
+st179,
+st180,
+st110,
+st110,
+st181,
+st182,
+st183,
+st184,
+st185,
+st186,
+st187,
+st188,
+st189,
+st190,
+st191,
+st192,
+st193,
+st194,
+st110,
+st110,
+st171,
+st171,
+st195,
+st196,
+st197,
+st198,
+st199,
+st200,
+st201,
+st202,
+st203,
+st204,
+st205,
+st87,
+st87,
+st206,
+st207,
+st207,
+st207,
+st207,
+st207,
+st208,
+st209,
+st209,
+st209,
+st209,
+st210,
+st211,
+st211,
+st211,
+st211,
+st212,
+st87,
+st87,
+st87,
+st87,
+st213,
+st214,
+st87,
+st87,
+st213,
+st87,
+st87,
+st215,
+st216,
+st217,
+st87,
+st87,
+st87,
+st216,
+st87,
+st87,
+st87,
+st218,
+st219,
+st220,
+st87,
+st221,
+st87,
+st87,
+st87,
+st87,
+st87,
+st222,
+st223,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st224,
+st225,
+st226,
+st227,
+st87,
+st87,
+st87,
+st87,
+st228,
+st229,
+st230,
+st231,
+st87,
+st232,
+st87,
+st233,
+st230,
+st234,
+st171,
+st171,
+st171,
+st235,
+st236,
+st237,
+st238,
+st239,
+st240,
+st238,
+st87,
+st87,
+st241,
+st87,
+st87,
+st242,
+st87,
+st87,
+st243,
+st110,
+st110,
+st110,
+st110,
+st110,
+st87,
+st244,
+st245,
+st246,
+st247,
+st171,
+st248,
+st249,
+st171,
+st171,
+st250,
+st251,
+st252,
+st253,
+st254,
+st254,
+st87,
+st255,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st256,
+st257,
+st87,
+st258,
+st259,
+st260,
+st261,
+st262,
+st263,
+st87,
+st264,
+st238,
+st110,
+st110,
+st110,
+st110,
+st87,
+st87,
+st265,
+st266,
+st267,
+st107,
+st87,
+st268,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st36,
+st36,
+st269,
+st270,
+st271,
+st272,
+st273,
+st274,
+st36,
+st275,
+st276,
+st277,
+st278,
+st279,
+st280,
+st281,
+st282,
+st283,
+st284,
+st285,
+st286,
+st287,
+st288,
+st289,
+st290,
+st291,
+st292,
+st293,
+st294,
+st295,
+st296,
+st297,
+st298,
+st299,
+st300,
+st301,
+st302,
+st303,
+st304,
+st305,
+st306,
+st307,
+st308,
+st309,
+st310,
+st311,
+st312,
+st313,
+st314,
+st315,
+st316,
+st317,
+st318,
+st319,
+st320,
+st321,
+st322,
+st323,
+st324,
+st325,
+st110,
+st326,
+st327,
+st328,
+st329,
+st330,
+st331,
+st332,
+st333,
+st334,
+st335,
+st336,
+st337,
+st338,
+st339,
+st254,
+st340,
+st341,
+st254,
+st342,
+st343,
+st344,
+st345,
+st346,
+st347,
+st344,
+st348,
+st349,
+st350,
+st351,
+st352,
+st353,
+st344,
+st344,
+st354,
+st344,
+st355,
+st254,
+st356,
+st254,
+st254,
+st254,
+st254,
+st357,
+st254,
+st358,
+st344,
+st359,
+st254,
+st360,
+st361,
+st110,
+st254,
+st254,
+st362,
+st110,
+st363,
+st110,
+st364,
+st365,
+st366,
+st367,
+st368,
+st369,
+st370,
+st371,
+st372,
+st373,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st374,
+st375,
+st254,
+st254,
+st376,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st377,
+st254,
+st254,
+st378,
+st254,
+st379,
+st380,
+st110,
+st110,
+st110,
+st381,
+st254,
+st382,
+st254,
+st383,
+st384,
+st385,
+st386,
+st373,
+st387,
+st254,
+st388,
+st389,
+st344,
+st390,
+st344,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st344,
+st344,
+st344,
+st344,
+st344,
+st344,
+st344,
+st344,
+st391,
+st392,
+st344,
+st344,
+st344,
+st393,
+st344,
+st394,
+st395,
+st344,
+st344,
+st344,
+st344,
+st344,
+st344,
+st396,
+st344,
+st344,
+st344,
+st344,
+st344,
+st397,
+st344,
+st344,
+st254,
+st254,
+st254,
+st344,
+st398,
+st399,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st400,
+st401,
+st402,
+st36,
+st36,
+st403,
+st404,
+st405,
+st406,
+st407,
+st408,
+st409,
+st410,
+st411,
+st412,
+st413,
+st36,
+st36,
+st414,
+st87,
+st87,
+st87,
+st415,
+st110,
+st87,
+st225,
+st416,
+st416,
+st416,
+st416,
+st43,
+st43,
+st417,
+st418,
+st419,
+st420,
+st110,
+st110,
+st110,
+st110,
+st254,
+st421,
+st254,
+st254,
+st254,
+st254,
+st254,
+st422,
+st423,
+st424,
+st425,
+st426,
+st427,
+st428,
+st429,
+st430,
+st431,
+st432,
+st433,
+st434,
+st435,
+st436,
+st110,
+st437,
+st438,
+st439,
+st440,
+st441,
+st442,
+st443,
+st444,
+st445,
+st171,
+st446,
+st447,
+st448,
+st449,
+st450,
+st451,
+st452,
+st453,
+st87,
+st454,
+st455,
+st456,
+st457,
+st458,
+st459,
+st460,
+st461,
+st87,
+st242,
+st254,
+st254,
+st380,
+st462,
+st463,
+st464,
+st465,
+st466,
+st467,
+st468,
+st469,
+st470,
+st471,
+st472,
+st473,
+st474,
+st475,
+st476,
+st477,
+st478,
+st479,
+st480,
+st481,
+st482,
+st483,
+st484,
+st485,
+st486,
+st487,
+st488,
+st489,
+st490,
+st491,
+st492,
+st493,
+st494,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st495,
+st254,
+st254,
+st254,
+st254,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st496,
+st110,
+st110,
+st110,
+st87,
+st497,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st244,
+st254,
+st254,
+st254,
+st362,
+st110,
+st110,
+st110,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st498,
+st87,
+st499,
+st110,
+st500,
+st501,
+st502,
+st503,
+st504,
+st505,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st42,
+st506,
+st507,
+st508,
+st509,
+st510,
+st511,
+st512,
+st513,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st514,
+st515,
+st87,
+st516,
+st110,
+st87,
+st87,
+st87,
+st517,
+st518,
+st87,
+st87,
+st519,
+st520,
+st238,
+st110,
+st110,
+st107,
+st87,
+st521,
+st87,
+st522,
+st523,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st87,
+st87,
+st524,
+st525,
+st526,
+st527,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st532,
+st530,
+st528,
+st529,
+st530,
+st531,
+st530,
+st533,
+st110,
+st110,
+st110,
+st110,
+st110,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st534,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st535,
+st536,
+st537,
+st538,
+st539,
+st540,
+st541,
+st542,
+st543,
+st544,
+st545,
+st546,
+st547,
+st548,
+st549,
+st550,
+st551,
+st552,
+st553,
+st554,
+st555,
+st556,
+st557,
+st558,
+st559,
+st560,
+st561,
+st562,
+st563,
+st564,
+st565,
+st110,
+st110,
+st566,
+st567,
+st568,
+st569,
+st570,
+st571,
+st572,
+st573,
+st574,
+st575,
+st576,
+st577,
+st110,
+st578,
+st579,
+st580,
+st581,
+st582,
+st583,
+st584,
+st585,
+st586,
+st587,
+st588,
+st589,
+st590,
+st591,
+st592,
+st593,
+st594,
+st595,
+st596,
+st597,
+st598,
+st599,
+st600,
+st110,
+st601,
+st602,
+st603,
+st604,
+st605,
+st606,
+st607,
+st608,
+st110,
+st110,
+st609,
+st610,
+st611,
+st280,
+st612,
+st613,
+st614,
+st615,
+st616,
+st617,
+st618,
+st619,
+st620,
+st621,
+st622,
+st623,
+st624,
+st625,
+st626,
+st627,
+st628,
+st629,
+st630,
+st631,
+st632,
+st633,
+st634,
+st635,
+st636,
+st637,
+st638,
+st639,
+st640,
+st641,
+st87,
+st217,
+st642,
+st454,
+st454,
+st110,
+st110,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st88,
+st643,
+st373,
+st373,
+st644,
+st645,
+st645,
+st645,
+st646,
+st647,
+st437,
+st110,
+st110,
+st110,
+st254,
+st254,
+st648,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st87,
+st244,
+st87,
+st87,
+st87,
+st136,
+st110,
+st110,
+st87,
+st649,
+st650,
+st87,
+st651,
+st110,
+st110,
+st110,
+st87,
+st652,
+st87,
+st87,
+st653,
+st654,
+st110,
+st110,
+st655,
+st656,
+st657,
+st36,
+st36,
+st87,
+st87,
+st87,
+st87,
+st454,
+st238,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st658,
+st87,
+st87,
+st659,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st87,
+st660,
+st87,
+st661,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st662,
+st663,
+st87,
+st664,
+st665,
+st666,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st87,
+st649,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st645,
+st645,
+st645,
+st645,
+st645,
+st645,
+st667,
+st668,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st669,
+st254,
+st254,
+st670,
+st254,
+st254,
+st671,
+st672,
+st673,
+st674,
+st254,
+st675,
+st676,
+st677,
+st378,
+st110,
+st110,
+st254,
+st254,
+st254,
+st254,
+st678,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st254,
+st254,
+st254,
+st254,
+st254,
+st362,
+st373,
+st679,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
 st680,
 st681,
 st682,
@@ -30675,20 +26992,39 @@ st685,
 st686,
 st687,
 st688,
-st689,
-st690,
-st691,
-st692,
-st693,
-st694,
-st695,
-st696,
-st697,
-st698,
+st689,
+st690,
+st691,
+st692,
+st680,
+st681,
+st682,
+st693,
+st694,
+st695,
+st696,
+st697,
+st698,
+st699,
+st700,
+st701,
+st702,
+st680,
+st681,
+st682,
+st683,
+st684,
+st695,
+st686,
+st687,
+st688,
 st699,
 st700,
 st701,
 st702,
+st680,
+st681,
+st682,
 st703,
 st704,
 st705,
@@ -30700,132 +27036,4785 @@ st710,
 st711,
 st712,
 st713,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
 st714,
-st77,
-st715,
-st715,
-st715,
 st715,
-st715,
-st715,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st77,
-st564,
-st564,
-st564,
-st564,
-st564,
-st564,
-st564,
-st564,
-st564,
-st564,
-st564,
-st564,
-st564,
-st564,
-st564,
-st77,
+st716,
+st717,
+st718,
+st719,
+st720,
+st721,
+st722,
+st723,
+st724,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st254,
+st254,
+st437,
+st254,
+st254,
+st254,
+st254,
+st254,
+st254,
+st380,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st171,
+st725,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st726,
+st727,
+st728,
+st729,
+st730,
+st731,
+st732,
+st733,
+st734,
+st735,
+st736,
+st737,
+st738,
+st739,
+st740,
+st741,
+st742,
+st743,
+st744,
+st745,
+st746,
+st747,
+st748,
+st749,
+st750,
+st751,
+st752,
+st753,
+st754,
+st755,
+st756,
+st757,
+st758,
+st759,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st760,
+st110,
+st761,
+st761,
+st761,
+st761,
+st761,
+st761,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st110,
+st610,
+st610,
+st610,
+st610,
+st610,
+st610,
+st610,
+st610,
+st610,
+st610,
+st610,
+st610,
+st610,
+st610,
+st610,
+st110,
 };
 const struct unicode_utf8_row unicode_utf8_valid[] = {
  { 1, 0, 0 }, /* 0 */
index 86f5fee..1d89322 100644 (file)
@@ -48,31 +48,40 @@ enum unicode_Grapheme_Break {
   unicode_Grapheme_Break_LV,
   unicode_Grapheme_Break_LVT,
   unicode_Grapheme_Break_Other,
+  unicode_Grapheme_Break_Prepend,
+  unicode_Grapheme_Break_SpacingMark,
   unicode_Grapheme_Break_T,
   unicode_Grapheme_Break_V
 };
 extern const char *const unicode_Grapheme_Break_names[];
 enum unicode_Word_Break {
   unicode_Word_Break_ALetter,
+  unicode_Word_Break_CR,
   unicode_Word_Break_Extend,
   unicode_Word_Break_ExtendNumLet,
   unicode_Word_Break_Format,
   unicode_Word_Break_Katakana,
+  unicode_Word_Break_LF,
   unicode_Word_Break_MidLetter,
   unicode_Word_Break_MidNum,
+  unicode_Word_Break_MidNumLet,
+  unicode_Word_Break_Newline,
   unicode_Word_Break_Numeric,
   unicode_Word_Break_Other
 };
 extern const char *const unicode_Word_Break_names[];
 enum unicode_Sentence_Break {
   unicode_Sentence_Break_ATerm,
+  unicode_Sentence_Break_CR,
   unicode_Sentence_Break_Close,
   unicode_Sentence_Break_Extend,
   unicode_Sentence_Break_Format,
+  unicode_Sentence_Break_LF,
   unicode_Sentence_Break_Lower,
   unicode_Sentence_Break_Numeric,
   unicode_Sentence_Break_OLetter,
   unicode_Sentence_Break_Other,
+  unicode_Sentence_Break_SContinue,
   unicode_Sentence_Break_STerm,
   unicode_Sentence_Break_Sep,
   unicode_Sentence_Break_Sp,
index e1740ac..7ee1f45 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -47,7 +47,7 @@ char *infer_url(int include_path_info) {
   /* Figure out the server.  'MUST' be set and we don't cope if it
    * is not. */
   if(!(server = getenv("SERVER_NAME")))
-    fatal(0, "SERVER_NAME is not set");
+    disorder_fatal(0, "SERVER_NAME is not set");
   server = xstrdup(server);
   
   /* Figure out the port.  'MUST' be set but we cope if it is not. */
@@ -67,7 +67,7 @@ char *infer_url(int include_path_info) {
   } else {
     /* RFC3875 s4.1.13 */
     if(!(script = getenv("SCRIPT_NAME")))
-      fatal(0, "SCRIPT_NAME is not set");
+      disorder_fatal(0, "SCRIPT_NAME is not set");
     /* SCRIPT_NAME may be "" */
     if(!*script)
       script = "/";
@@ -78,7 +78,7 @@ char *infer_url(int include_path_info) {
                      script, urlencodestring(path_info));
   }
   if(script[0] != '/')
-    fatal(0, "SCRIPT_NAME does not start with a '/'");
+    disorder_fatal(0, "SCRIPT_NAME does not start with a '/'");
   script = xstrdup(script);
   
   if(port == 80)
index 488caf9..100da16 100644 (file)
@@ -42,21 +42,30 @@ void become_mortal(void) {
   
   if(config->user) {
     if(!(pw = getpwnam(config->user)))
-      fatal(0, "cannot find user %s", config->user);
+      disorder_fatal(0, "cannot find user %s", config->user);
     if(pw->pw_uid != getuid()) {
       if(initgroups(config->user, pw->pw_gid))
-       fatal(errno, "error calling initgroups");
-      if(setgid(pw->pw_gid) < 0) fatal(errno, "error calling setgid");
-      if(setuid(pw->pw_uid) < 0) fatal(errno, "error calling setgid");
-      info("changed to user %s (uid %lu)", config->user, (unsigned long)getuid());
+       disorder_fatal(errno, "error calling initgroups");
+      if(setgid(pw->pw_gid) < 0)
+        disorder_fatal(errno, "error calling setgid");
+      if(setuid(pw->pw_uid) < 0)
+        disorder_fatal(errno, "error calling setgid");
+      disorder_info("changed to user %s (uid %lu)",
+                    config->user, (unsigned long)getuid());
     }
     /* sanity checks */
-    if(getuid() != pw->pw_uid) fatal(0, "wrong real uid");
-    if(geteuid() != pw->pw_uid) fatal(0, "wrong effective uid");
-    if(getgid() != pw->pw_gid) fatal(0, "wrong real gid");
-    if(getegid() != pw->pw_gid) fatal(0, "wrong effective gid");
-    if(setuid(0) != -1) fatal(0, "setuid(0) unexpectedly succeeded");
-    if(seteuid(0) != -1) fatal(0, "seteuid(0) unexpectedly succeeded");
+    if(getuid() != pw->pw_uid)
+      disorder_fatal(0, "wrong real uid");
+    if(geteuid() != pw->pw_uid)
+      disorder_fatal(0, "wrong effective uid");
+    if(getgid() != pw->pw_gid)
+      disorder_fatal(0, "wrong real gid");
+    if(getegid() != pw->pw_gid)
+      disorder_fatal(0, "wrong effective gid");
+    if(setuid(0) != -1)
+      disorder_fatal(0, "setuid(0) unexpectedly succeeded");
+    if(seteuid(0) != -1)
+      disorder_fatal(0, "seteuid(0) unexpectedly succeeded");
   }
 }
 
@@ -84,13 +93,13 @@ void make_home(void) {
     }
     /* create the directory itself */
     if(mkdir(config->home, 02755) < 0)
-      fatal(errno, "error creating %s", config->home);
+      disorder_fatal(errno, "error creating %s", config->home);
     /* make sure it has the right ownership */
     if(config->user) {
       if(!(pw = getpwnam(config->user)))
-        fatal(0, "cannot find user %s", config->user);
+        disorder_fatal(0, "cannot find user %s", config->user);
       if(chown(config->home, pw->pw_uid, pw->pw_gid) < 0)
-        fatal(errno, "error chowning %s", config->home);
+        disorder_fatal(errno, "error chowning %s", config->home);
     }
   }
 }
diff --git a/lib/validity.c b/lib/validity.c
new file mode 100644 (file)
index 0000000..408c4d5
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * 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 program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+/** @file lib/validity.c
+ * @brief Various validity checks
+ */
+#include "common.h"
+#include "validity.h"
+
+#include "mem.h"
+
+/** @brief Parse a playlist name
+ * @param name Playlist name
+ * @param ownerp Where to put owner, or NULL
+ * @param sharep Where to put default sharing, or NULL
+ * @return 0 on success, -1 on error
+ *
+ * Playlists take the form USER.PLAYLIST or just PLAYLIST.  The PLAYLIST part
+ * is alphanumeric and nonempty.  USER is a username (see valid_username()).
+ */
+int playlist_parse_name(const char *name,
+                        char **ownerp,
+                        char **sharep) {
+  const char *dot = strchr(name, '.'), *share;
+  char *owner;
+
+  if(dot) {
+    /* Owned playlist */
+    owner = xstrndup(name, dot - name);
+    if(!valid_username(owner))
+      return -1;
+    if(!valid_username(dot + 1))
+      return -1;
+    share = "private";
+  } else {
+    /* Shared playlist */
+    if(!valid_username(name))
+      return -1;
+    owner = 0;
+    share = "shared";
+  }
+  if(ownerp)
+    *ownerp = owner;
+  if(sharep)
+    *sharep = xstrdup(share);
+  return 0;
+}
+
+/** @brief Return non-zero for a valid username
+ * @param user Candidate username
+ * @return Nonzero if it's valid
+ *
+ * Currently we only allow the letters and digits in ASCII.  We could be more
+ * liberal than this but it is a nice simple test.  It is critical that
+ * semicolons are never allowed.
+ *
+ * NB also used by playlist_parse_name() to validate playlist names!
+ */
+int valid_username(const char *user) {
+  if(!*user)
+    return 0;
+  while(*user) {
+    const uint8_t c = *user++;
+    /* For now we are very strict */
+    if((c >= 'a' && c <= 'z')
+       || (c >= 'A' && c <= 'Z')
+       || (c >= '0' && c <= '9'))
+      /* ok */;
+    else
+      return 0;
+  }
+  return 1;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/validity.h b/lib/validity.h
new file mode 100644 (file)
index 0000000..8ce2505
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * 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 program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+/** @file lib/validity.c
+ * @brief Various validity checks
+ */
+#ifndef VALIDITY_H
+#define VALIDITY_H
+
+#include "common.h"
+#include "validity.h"
+
+int playlist_parse_name(const char *name,
+                        char **ownerp,
+                        char **sharep);
+int valid_username(const char *user);
+
+#endif /* VALIDITY_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index dec036e..0ac2c17 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004, 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2004, 2007-2009 Richard Kettlewell
  *
  * 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
@@ -32,7 +32,7 @@
  * @param w Exit status (e.g. from waitpid())
  * @return Allocated string containing description of status
  */
-const char *wstat(int w) {
+char *wstat(int w) {
   int n;
   char *r;
 
@@ -48,7 +48,7 @@ const char *wstat(int w) {
   else
     n = byte_xasprintf(&r, "terminated with unknown wait status %#x",
                      (unsigned)w);
-  return n >= 0 ? r : "[could not convert wait status]";
+  return n >= 0 ? r : xstrdup("[could not convert wait status]");
 }
 
 /*
index aebe6ef..b6deb8a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004, 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2004, 2007-2009 Richard Kettlewell
  *
  * 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
@@ -24,7 +24,7 @@
 
 #include <sys/wait.h>
 
-const char *wstat(int w);
+char *wstat(int w);
 /* Format wait status @w@.  In extremis the return value might be a
  * pointer to a string literal.  The result should always be ASCII. */
 
index 2f81f20..de3562b 100644 (file)
@@ -1,6 +1,6 @@
 #
 # This file is part of DisOrder.
-# Copyright (C) 2008 Richard Kettlewell
+# Copyright (C) 2009 Richard Kettlewell
 #
 # 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
@@ -20,7 +20,8 @@ TESTS=t-addr t-arcfour t-basen t-bits t-cache t-casefold t-charset    \
        t-cookies t-dateparse t-event t-filepart t-hash t-heap t-hex    \
        t-kvp t-mime t-printf t-regsub t-selection t-signame t-sink     \
        t-split t-syscalls t-trackname t-unicode t-url t-utf8 t-vector  \
-       t-words t-wstat t-macros t-cgi t-eventdist
+       t-words t-wstat t-macros t-cgi t-eventdist t-resample           \
+       t-configuration
 
 noinst_PROGRAMS=$(TESTS)
 
@@ -61,6 +62,10 @@ t_vector_SOURCES=t-vector.c test.c test.h
 t_words_SOURCES=t-words.c test.c test.h
 t_wstat_SOURCES=t-wstat.c test.c test.h
 t_eventdist_SOURCES=t-eventdist.c test.c test.h
+t_resample_SOURCES=t-resample.c test.c test.h
+t_resample_LDFLAGS=$(LIBSAMPLERATE)
+t_configuration_SOURCES=t-configuration.c test.c test.h
+t_configuration_LDFLAGS=$(LIBGCRYPT)
 
 check-report: before-check check make-coverage-reports
 before-check:
index 7710212..e33a1d6 100644 (file)
@@ -33,6 +33,7 @@ static void test_addr(void) {
   };
 
   struct sockaddr_in a1 = {
+    .sin_family = AF_INET,
     .sin_port = ntohs(25),
     .sin_addr = { .s_addr = 0}
   };
@@ -45,6 +46,7 @@ static void test_addr(void) {
   };
 
   struct sockaddr_in a2 = {
+    .sin_family = AF_INET,
     .sin_port = ntohs(119),
     .sin_addr = { .s_addr = htonl(0x7F000001) }
   };
index 715490c..3cbc044 100644 (file)
 #include "test.h"
 
 static void test_basen(void) {
-  unsigned long v[64];
+  uint32_t v[64];
   char buffer[1024];
 
   v[0] = 999;
   insist(basen(v, 1, buffer, sizeof buffer, 10) == 0);
   check_string(buffer, "999");
+  memset(v, 0xFF, sizeof v);
+  insist(nesab(v, 1, buffer, 10) == 0);
+  check_integer(v[0], 999);
+  check_integer(v[1], 0xFFFFFFFF);
+  insist(nesab(v, 4, buffer, 10) == 0);
+  check_integer(v[0], 0);
+  check_integer(v[1], 0);
+  check_integer(v[2], 0);
+  check_integer(v[3], 999);
+  check_integer(v[4], 0xFFFFFFFF);
 
   v[0] = 1+2*7+3*7*7+4*7*7*7;
   insist(basen(v, 1, buffer, sizeof buffer, 7) == 0);
@@ -35,6 +45,24 @@ static void test_basen(void) {
   v[3] = 0x0C0D0E0F;
   insist(basen(v, 4, buffer, sizeof buffer, 256) == 0);
   check_string(buffer, "123456789abcdef");
+  memset(v, 0xFF, sizeof v);
+  insist(nesab(v, 4, buffer, 256) == 0);
+  check_integer(v[0], 0x00010203);
+  check_integer(v[1], 0x04050607);
+  check_integer(v[2], 0x08090A0B);
+  check_integer(v[3], 0x0C0D0E0F);
+  check_integer(v[4], 0xFFFFFFFF);
+  memset(v, 0xFF, sizeof v);
+  insist(nesab(v, 8, buffer, 256) == 0);
+  check_integer(v[0], 0);
+  check_integer(v[1], 0);
+  check_integer(v[2], 0);
+  check_integer(v[3], 0);
+  check_integer(v[4], 0x00010203);
+  check_integer(v[5], 0x04050607);
+  check_integer(v[6], 0x08090A0B);
+  check_integer(v[7], 0x0C0D0E0F);
+  check_integer(v[8], 0xFFFFFFFF);
 
   v[0] = 0x00010203;
   v[1] = 0x04050607;
@@ -42,6 +70,24 @@ static void test_basen(void) {
   v[3] = 0x0C0D0E0F;
   insist(basen(v, 4, buffer, sizeof buffer, 16) == 0);
   check_string(buffer, "102030405060708090a0b0c0d0e0f");
+  memset(v, 0xFF, sizeof v);
+  insist(nesab(v, 4, buffer, 16) == 0);
+  check_integer(v[0], 0x00010203);
+  check_integer(v[1], 0x04050607);
+  check_integer(v[2], 0x08090A0B);
+  check_integer(v[3], 0x0C0D0E0F);
+  check_integer(v[4], 0xFFFFFFFF);
+  memset(v, 0xFF, sizeof v);
+  insist(nesab(v, 8, buffer, 16) == 0);
+  check_integer(v[0], 0);
+  check_integer(v[1], 0);
+  check_integer(v[2], 0);
+  check_integer(v[3], 0);
+  check_integer(v[4], 0x00010203);
+  check_integer(v[5], 0x04050607);
+  check_integer(v[6], 0x08090A0B);
+  check_integer(v[7], 0x0C0D0E0F);
+  check_integer(v[8], 0xFFFFFFFF);
 
   v[0] = 0x00010203;
   v[1] = 0x04050607;
index 109a38a..f621495 100644 (file)
@@ -26,7 +26,7 @@ static void input_from(const char *s) {
      || fputs("wibble wibble\r\nspong", fp) < 0 /* ensure CONTENT_LENGTH
                                                  * honored */
      || fflush(fp) < 0)
-    fatal(errno, "writing to temporary file");
+    disorder_fatal(errno, "writing to temporary file");
   rewind(fp);
   xdup2(fileno(fp), 0);
   lseek(0, 0/*offset*/, SEEK_SET);
similarity index 79%
rename from server/api-client.h
rename to libtests/t-configuration.c
index 99a9410..4ff7b18 100644 (file)
@@ -1,12 +1,12 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004, 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2010 Richard Kettlewell
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
- *
+ * 
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-#ifndef API_CLIENT_H
-#define API_CLIENT_H
+#include "test.h"
 
-disorder_client *disorder_get_client(void);
+static void test_configuration(void) {
+  insist(config_verify() == 0);
+}
 
-#endif /* API_CLIENT_H */
+TEST(configuration);
 
 /*
 Local Variables:
 c-basic-offset:2
 comment-column:40
+fill-column:79
+indent-tabs-mode:nil
 End:
 */
index a0a3b0d..7926e01 100644 (file)
@@ -33,7 +33,7 @@ static void check_date(time_t when,
 }
 
 static void test_dateparse(void) {
-  time_t now = time(0);
+  time_t now = xtime(0);
   check_date(now, "%Y-%m-%d %H:%M:%S", localtime);
 #if 0         /* see dateparse.c */
   check_date(now, "%Y-%m-%d %H:%M:%S %Z", localtime);
index 87c803a..1474408 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2008 Richard Kettlewell
+ * Copyright (C) 2008, 2009 Richard Kettlewell
  *
  * 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
@@ -19,6 +19,7 @@
 #include "event.h"
 
 #include <time.h>
+#include <sys/time.h>
 
 static int run1, run2, run3;
 static ev_timeout_handle t1, t2, t3;
@@ -50,13 +51,13 @@ static void test_event(void) {
   ev_source *ev;
 
   ev = ev_new();
-  w.tv_sec = time(0) + 2;
+  w.tv_sec = xtime(0) + 2;
   w.tv_usec = 0;
   ev_timeout(ev, &t1, &w, callback1, 0);
-  w.tv_sec = time(0) + 3;
+  w.tv_sec = xtime(0) + 3;
   w.tv_usec = 0;
   ev_timeout(ev, &t2, &w, callback2, 0);
-  w.tv_sec = time(0) + 4;
+  w.tv_sec = xtime(0) + 4;
   w.tv_usec = 0;
   ev_timeout(ev, &t3, &w, callback3, 0);
   check_integer(ev_run(ev), 1);
diff --git a/libtests/t-resample.c b/libtests/t-resample.c
new file mode 100644 (file)
index 0000000..2eab79a
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "test.h"
+#include "resample.h"
+#include "vector.h"
+
+/* Accumulate converted bytes in a dynamic string */
+static void converted(uint8_t *bytes,
+                      size_t nbytes,
+                      void *cd) {
+  struct dynstr *d = cd;
+  dynstr_append_bytes(d, (void *)bytes, nbytes);
+}
+
+/* Converter wrapper */
+static uint8_t *convert(const struct resampler *rs,
+                        const uint8_t *input, size_t input_bytes,
+                        size_t *output_bytes) {
+  struct dynstr d[1];
+
+  dynstr_init(d);
+  while(input_bytes > 0) {
+    size_t chunk = input_bytes > 1024 ? 1024 : input_bytes;
+    size_t consumed = resample_convert(rs,
+                                       input, input_bytes,
+                                       input_bytes == chunk,
+                                       converted,
+                                       d);
+    input += consumed;
+    input_bytes -= consumed;
+  }
+  *output_bytes = d->nvec;
+  return (uint8_t *)d->vec;
+}
+
+static const struct {
+  const char *description;
+  int input_bits;
+  int input_channels;
+  int input_rate;
+  int input_signed;
+  int input_endian;
+  const char *input;
+  size_t input_bytes;
+  int output_bits;
+  int output_channels;
+  int output_rate;
+  int output_signed;
+  int output_endian;
+  const char *output;
+  size_t output_bytes;
+} conversions[] = {
+  /* Conversions that don't change the sample rate */
+  {
+    "empty input",
+    8, 1, 8000, 0, ENDIAN_LITTLE, "", 0,
+    8, 1, 8000, 0, ENDIAN_LITTLE, "", 0
+  },
+  {
+    "sign flip 8-bit unsigned->signed",
+    8, 1, 8000, 0, ENDIAN_LITTLE, "\x00\x7F\x80\xFF", 4,
+    8, 1, 8000, 1, ENDIAN_LITTLE, "\x80\xFF\x00\x7F", 4
+  },
+  {
+    "sign flip 8-bit signed->unsigned",
+    8, 1, 8000, 1, ENDIAN_BIG, "\x80\xFF\x00\x7F", 4,
+    8, 1, 8000, 0, ENDIAN_BIG, "\x00\x7F\x80\xFF", 4
+  },
+  {
+    "mono to stereo",
+    8, 1, 8000, 0, ENDIAN_LITTLE, "\x00\x7F\x80\xFF", 4,
+    8, 2, 8000, 0, ENDIAN_LITTLE, "\x00\x00\x7F\x7F\x80\x80\xFF\xFF", 8
+  },
+  {
+    "stereo to mono",
+    8, 2, 8000, 0, ENDIAN_LITTLE, "\x00\x01\x7F\x02\x80\x03\xFF\x04", 8,
+    8, 1, 8000, 0, ENDIAN_LITTLE, "\x00\x7F\x80\xFF", 4
+  },
+  {
+    "endian flip little->big",
+    16, 1, 8000, 0, ENDIAN_LITTLE, "\x00\x01\x00\xFF\x01\x00\x01\xFF", 8,
+    16, 1, 8000, 0, ENDIAN_BIG, "\x01\x00\xFF\x00\x00\x01\xFF\x01", 8,
+  },
+  {
+    "endian flip big->little",
+    16, 1, 8000, 0, ENDIAN_BIG, "\x01\x00\xFF\x00\x00\x01\xFF\x01", 8,
+    16, 1, 8000, 0, ENDIAN_LITTLE, "\x00\x01\x00\xFF\x01\x00\x01\xFF", 8,
+  },
+  {
+    "8-bit to 16-bit",
+    8, 1, 8000, 0, ENDIAN_BIG, "\x00\x7F\x80\xFF", 4,
+    16, 1, 8000, 0, ENDIAN_BIG, "\x00\x00\x7F\x00\x80\x00\xFF\x00", 8
+  },
+  {
+    "16-bit to 8-bit",
+    16, 1, 8000, 0, ENDIAN_BIG, "\x00\x00\x7F\xFF\x80\x00\xFF\xFF", 8,
+    8, 1, 8000, 0, ENDIAN_BIG, "\x00\x7F\x80\xFF", 4
+  },
+#if HAVE_SAMPLERATE_H
+  /* Conversions that do change the sample rate */
+  
+#endif
+};
+#define NCONVERSIONS (sizeof conversions / sizeof *conversions)
+
+static void test_resample(void) {
+  for(size_t n = 0; n < NCONVERSIONS; ++n) {
+    struct resampler rs[1];
+
+    resample_init(rs, 
+                  conversions[n].input_bits,
+                  conversions[n].input_channels,
+                  conversions[n].input_rate,
+                  conversions[n].input_signed,
+                  conversions[n].input_endian,
+                  conversions[n].output_bits,
+                  conversions[n].output_channels,
+                  conversions[n].output_rate,
+                  conversions[n].output_signed,
+                  conversions[n].output_endian);
+    size_t output_bytes;
+    const uint8_t *output = convert(rs,
+                                    (const uint8_t *)conversions[n].input, 
+                                    conversions[n].input_bytes, 
+                                    &output_bytes);
+    if(output_bytes != conversions[n].output_bytes
+       || memcmp(output, conversions[n].output, output_bytes)) {
+      fprintf(stderr, "index %zu description %s mismatch\n",
+              n, conversions[n].description);
+      size_t k = 0;
+      while(k < conversions[n].output_bytes || k < output_bytes) {
+        size_t j = 0;
+        fprintf(stderr, "%8zu E:", k);
+        for(j = 0; j < 16; ++j) {
+          if(j + k < conversions[n].output_bytes)
+            fprintf(stderr, " %02x", conversions[n].output[j + k]);
+        }
+        fprintf(stderr, "\n         G:");
+        for(j = 0; j < 16; ++j) {
+          if(j + k < output_bytes)
+            fprintf(stderr, " %02x", output[j + k]);
+        }
+        fprintf(stderr, "\n");
+        k += 16;
+      }
+      ++errors;
+    }
+    ++tests;
+  }
+}
+
+TEST(resample);
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index ba6bba5..1505844 100644 (file)
@@ -30,13 +30,13 @@ static FILE *open_unicode_test(const char *path) {
     base = path;
   if(!(fp = fopen(base, "r"))) {
     snprintf(buffer, sizeof buffer,
-             "wget http://www.unicode.org/Public/5.0.0/ucd/%s", path);
+             "wget http://www.unicode.org/Public/5.1.0/ucd/%s", path);
     if((w = system(buffer)))
-      fatal(0, "%s: %s", buffer, wstat(w));
+      disorder_fatal(0, "%s: %s", buffer, wstat(w));
     if(chmod(base, 0444) < 0)
-      fatal(errno, "chmod %s", base);
+      disorder_fatal(errno, "chmod %s", base);
     if(!(fp = fopen(base, "r")))
-      fatal(errno, "%s", base);
+      disorder_fatal(errno, "%s", base);
   }
   return fp;
 }
@@ -79,7 +79,7 @@ static void breaktest(const char *path,
         buffer[bn++] = strtoul(lp, &lp, 16);
         continue;
       }
-      fatal(0, "%s:%d: evil line: %s", path, lineno, l);
+      disorder_fatal(0, "%s:%d: evil line: %s", path, lineno, l);
     }
     for(n = 0; n <= bn; ++n) {
       if(breakfn(buffer, bn, n) != break_allowed[n]) {
index 73bfdd2..354106b 100644 (file)
@@ -102,7 +102,8 @@ uint32_t *ucs4parse(const char *s) {
   while(*s) {
     errno = 0;
     dynstr_ucs4_append(&d, strtoul(s, &e, 0));
-    if(errno) fatal(errno, "strtoul (%s)", s);
+    if(errno)
+      disorder_fatal(errno, "strtoul (%s)", s);
     s = e;
   }
   dynstr_ucs4_terminate(&d);
index ae3f8eb..7852152 100644 (file)
@@ -25,7 +25,7 @@ notify_la_LDFLAGS=-module
 
 disorder_tracklength_la_SOURCES=tracklength.c mad.c madshim.h ../lib/wav.h ../lib/wav.c
 disorder_tracklength_la_LDFLAGS=-module
-disorder_tracklength_la_LIBADD=$(LIBVORBISFILE) $(LIBMAD) $(LIBFLAC)
+disorder_tracklength_la_LIBADD=$(LIBVORBISFILE) $(LIBMAD) $(LIBFLAC) -lm
 
 fs_la_SOURCES=fs.c
 fs_la_LDFLAGS=-module
index e873e49..fe054a9 100644 (file)
@@ -50,7 +50,7 @@ import os
 import pwd
 import socket
 import binascii
-import sha
+import hashlib
 import sys
 import locale
 
@@ -66,6 +66,18 @@ _unquoted = re.compile("[^\"' \\t\\n\\r][^ \t\n\r]*")
 
 _response = re.compile("([0-9]{3}) ?(.*)")
 
+# hashes
+_hashes = {
+  "sha1": hashlib.sha1,
+  "SHA1": hashlib.sha1,
+  "sha256": hashlib.sha256,
+  "SHA256": hashlib.sha256,
+  "sha384": hashlib.sha384,
+  "SHA384": hashlib.sha384,
+  "sha512": hashlib.sha512,
+  "SHA512": hashlib.sha512,
+};
+
 version = "_version_"
 
 ########################################################################
@@ -113,8 +125,8 @@ class operationError(Error):
     self.cmd_ = cmd
     self.details_ = details
   def __str__(self):
-    """Return the complete response string from the server, with the command
-    if available.
+    """Return the complete response string from the server, with the
+    command if available.
 
     Excludes the final newline.
     """
@@ -387,8 +399,7 @@ class client:
             password = self.config['password']
           else:
             password = self.password
-          # TODO support algorithms other than SHA-1
-          h = sha.sha()
+          h = _hashes[algo]()
           h.update(password)
           h.update(binascii.unhexlify(challenge))
           self._simple("user", user, h.hexdigest())
@@ -422,12 +433,22 @@ class client:
 
     Returns the ID of the new queue entry.
 
-    Note that queue IDs are unicode strings (because all track information
-    values are unicode strings).
+    Note that queue IDs are unicode strings (because all track
+    information values are unicode strings).
     """
     res, details = self._simple("play", track)
     return unicode(details)             # because it's unicode in queue() output
 
+  def playafter(self, target, tracks):
+    """Insert tracks into a specific point in the queue.
+
+    Arguments:
+    target -- target ID or None to insert at start of queue
+    tracks -- a list of tracks to play"""
+    if target is None:
+      target = ''
+    self._simple("playafter", target, *tracks)
+
   def remove(self, track):
     """Remove a track from the queue.
 
@@ -528,8 +549,8 @@ class client:
     The return value is a list of dictionaries corresponding to
     recently played tracks.  The next track to be played comes first.
 
-    See disorder_protocol(5) for the meanings of the keys.  All keys are
-    plain strings but the values will be unicode strings."""
+    See disorder_protocol(5) for the meanings of the keys.
+    All keys are plain strings but the values will be unicode strings."""
     return self._somequeue("queue")
 
   def _somedir(self, command, dir, re):
@@ -764,7 +785,8 @@ class client:
     
     The callback should return True to continue or False to stop (don't
     forget this, or your program will mysteriously misbehave).  Once you
-    stop reading the log the connection is useless and should be deleted.
+    stop reading the log the connection is useless and should be
+    deleted.
 
     It is suggested that you use the disorder.monitor class instead of
     calling this method directly, but this is not mandatory.
@@ -891,7 +913,8 @@ class client:
     self._simple("schedule-del", event)
 
   def schedule_get(self, event):
-    """Get the details for an event as a dict (returns None if event not found)"""
+    """Get the details for an event as a dict (returns None if
+    event not found)"""
     res, details = self._simple("schedule-get", event)
     if res == 555:
       return None
@@ -909,6 +932,54 @@ class client:
     """Adopt a randomly picked track"""
     self._simple("adopt", id)
 
+  def playlist_delete(self, playlist):
+    """Delete a playlist"""
+    res, details = self._simple("playlist-delete", playlist)
+    if res == 555:
+      raise operationError(res, details, "playlist-delete")
+
+  def playlist_get(self, playlist):
+    """Get the contents of a playlist
+
+    The return value is an array of track names, or None if there is no
+    such playlist."""
+    res, details = self._simple("playlist-get", playlist)
+    if res == 555:
+      return None
+    return self._body()
+
+  def playlist_lock(self, playlist):
+    """Lock a playlist.  Playlists can only be modified when locked."""
+    self._simple("playlist-lock", playlist)
+
+  def playlist_unlock(self):
+    """Unlock the locked playlist."""
+    self._simple("playlist-unlock")
+
+  def playlist_set(self, playlist, tracks):
+    """Set the contents of a playlist.  The playlist must be locked.
+
+    Arguments:
+    playlist -- Playlist to set
+    tracks -- Array of tracks"""
+    self._simple_body(tracks, "playlist-set", playlist)
+
+  def playlist_set_share(self, playlist, share):
+    """Set the sharing status of a playlist"""
+    self._simple("playlist-set-share", playlist, share)
+
+  def playlist_get_share(self, playlist):
+    """Returns the sharing status of a playlist"""
+    res, details = self._simple("playlist-get-share", playlist)
+    if res == 555:
+      return None
+    return _split(details)[0]
+
+  def playlists(self):
+    """Returns the list of visible playlists"""
+    self._simple("playlists")
+    return self._body()
+
   ########################################################################
   # I/O infrastructure
 
@@ -938,8 +1009,8 @@ class client:
     else:
       raise protocolError(self.who, "invalid response %s")
 
-  def _send(self, *command):
-    # Quote and send a command
+  def _send(self, body, *command):
+    # Quote and send a command and optional body
     #
     # Returns the encoded command.
     quoted = _quote(command)
@@ -948,6 +1019,13 @@ class client:
     try:
       self.w.write(encoded)
       self.w.write("\n")
+      if body != None:
+        for l in body:
+          if l[0] == ".":
+            self.w.write(".")
+          self.w.write(l)
+          self.w.write("\n")
+        self.w.write(".\n")
       self.w.flush()
       return encoded
     except IOError, e:
@@ -958,7 +1036,7 @@ class client:
       self._disconnect()
       raise
 
-  def _simple(self, *command):
+  def _simple(self, *command): 
     # Issue a simple command, throw an exception on error
     #
     # If an I/O error occurs, disconnect from the server.
@@ -966,10 +1044,20 @@ class client:
     # On success or 'normal' errors returns response as a (code, details) tuple
     #
     # On error raise operationError
+    return self._simple_body(None, *command)
+  def _simple_body(self, body, *command):
+    # Issue a simple command with optional body, throw an exception on error
+    #
+    # If an I/O error occurs, disconnect from the server.
+    #
+    # On success or 'normal' errors returns response as a (code, details) tuple
+    #
+    # On error raise operationError
     if self.state == 'disconnected':
       self.connect()
     if command:
-      cmd = self._send(*command)
+      cmd = self._send(body, *command)
     else:
       cmd = None
     res, details = self._response()
@@ -1050,8 +1138,8 @@ class client:
 class monitor:
   """DisOrder event log monitor class
 
-  Intended to be subclassed with methods corresponding to event log messages
-  the implementor cares about over-ridden."""
+  Intended to be subclassed with methods corresponding to event log
+  messages the implementor cares about over-ridden."""
 
   def __init__(self, c=None):
     """Constructor for the monitor class
@@ -1067,8 +1155,8 @@ class monitor:
 
   def run(self):
     """Start monitoring logs.  Continues monitoring until one of the
-    message-specific methods returns False.  Can be called more than once
-    (but not recursively!)"""
+    message-specific methods returns False.  Can be called more than
+    once (but not recursively!)"""
     self.c.log(self._callback)
 
   def when(self):
index 89eb63d..2766b17 100644 (file)
@@ -33,6 +33,7 @@ complete -o default \
              setup-guest schedule-del schedule-list
              schedule-set-global schedule-unset-global schedule-play
              adopt
+             playlist-del playlist-get playlist-set playlists
              -h --help -H --help-commands --version -V --config -c
              --length --debug -d" \
         disorder
index e899cc0..f428eef 100755 (executable)
@@ -1,7 +1,7 @@
 #! /bin/bash
 #
 # This file is part of DisOrder
-# Copyright (C) 2005-2008 Richard Kettlewell
+# Copyright (C) 2005-2009 Richard Kettlewell
 #
 # 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
@@ -56,9 +56,6 @@ build() {
 
 # Build various debs
 
-# Ubuntu dapper; binaries suit Debian etch too
-build dekabrach etch etch oldstable
-
 # Debian lenny (32-bit)
 build leucomorph lenny "" stable
 
index 21452dc..5290d1b 100755 (executable)
@@ -1,7 +1,7 @@
 #! /bin/sh
 #
 # This file is part of DisOrder
-# Copyright (C) 2004, 2005, 2007, 2008 Richard Kettlewell
+# Copyright (C) 2004, 2005, 2007, 2008, 2010 Richard Kettlewell
 #
 # 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
 set -e
 
 stdhead=false
+extension="html"
 
 while test $# -gt 0; do
   case "$1" in
   -stdhead )
     stdhead=true
     ;;
+  -extension )
+    shift
+    extension=$1
+    ;;
+  -- )
+    shift
+    break
+    ;;
   -* )
     echo >&2 "ERROR: unknown option $1"
     exit 1
     ;;
   * )
     break
+    ;;
   esac
   shift
 done
 
-title=$(basename $1)
-
-echo "<html>"
-echo " <head>"
-if $stdhead; then
-  echo "@quiethead@#"
-fi
-echo "  <title>$title</title>"
-echo " </head>"
-echo " <body>"
-if $stdhead; then
-  echo "@stdmenu{}@#"
-fi
-printf "   <pre class=manpage>"
-# this is kind of painful using only BREs
-nroff -Tascii -man "$1" | ${GNUSED} \
-                      '1d;$d;
-                       1,/./{/^$/d};
-                       s/&/\&amp;/g;
-                       s/</\&lt;/g;
-                       s/>/\&gt;/g;
-                       s/@/\&#64;/g;
-                       s!\(.\)\b\1!<b>\1</b>!g;
-                       s!\(&[#0-9a-z][0-9a-z]*;\)\b\1!<b>\1</b>!g;
-                       s!_\b\(.\)!<i>\1</i>!g;
-                       s!_\b\(&[#0-9a-z][0-9a-z]*;\)!<i>\1</i>!g;
-                       s!</\([bi]\)><\1>!!g'
-echo "</pre>"
-if $stdhead; then
-  echo "@credits"
-fi
-echo " </body>"
-echo "</html>"
+for page; do
+  title=$(basename $page)
+  output=$(basename $page).$extension
+  echo "$page -> $output" >&2
+  exec > $output.new
+  echo "<html>"
+  echo " <head>"
+  if $stdhead; then
+    echo "@quiethead@#"
+  fi
+  echo "  <title>$title</title>"
+  echo " </head>"
+  echo " <body>"
+  if $stdhead; then
+    echo "@stdmenu{}@#"
+  fi
+  printf "   <pre class=manpage>"
+  # this is kind of painful using only BREs
+  nroff -Tascii -man "$page" | ${GNUSED} \
+                        '1d;$d;
+                         1,/./{/^$/d};
+                         s/&/\&amp;/g;
+                         s/</\&lt;/g;
+                         s/>/\&gt;/g;
+                         s/@/\&#64;/g;
+                         s!\(.\)\b\1!<b>\1</b>!g;
+                         s!\(&[#0-9a-z][0-9a-z]*;\)\b\1!<b>\1</b>!g;
+                         s!_\b\(.\)!<i>\1</i>!g;
+                         s!_\b\(&[#0-9a-z][0-9a-z]*;\)!<i>\1</i>!g;
+                         s!</\([bi]\)><\1>!!g'
+  echo "</pre>"
+  if $stdhead; then
+    echo "@credits"
+  fi
+  echo " </body>"
+  echo "</html>"
+  mv $output.new $output
+done
index d1490cc..d43533e 100755 (executable)
@@ -92,7 +92,7 @@ sub input {
     my $path = shift;
     my $lpath = basename($path);
     if(!-e $lpath) {
-       system("wget http://www.unicode.org/Public/5.0.0/ucd/$path");
+       system("wget http://www.unicode.org/Public/5.1.0/ucd/$path");
        chmod(0444, $lpath) or die "$lpath: $!\n";
     }
     open(STDIN, "<$lpath") or die "$lpath: $!\n";
index 5a28fac..a02e753 100644 (file)
@@ -23,6 +23,7 @@ $(SEDFILES) : % : %.in Makefile
            -e 's!pkgconfdir!${sysconfdir}/disorder!g;' \
            -e 's!pkgstatedir!${localstatedir}/disorder!g;' \
            -e 's!pkgdatadir!${pkgdatadir}!g;' \
+           -e 's!dochtmldir!${dochtmldir}!g;' \
            -e 's!SENDMAIL!${SENDMAIL}!g;' \
            -e 's!_version_!${VERSION}!g;' \
                < $< > $@.new
index 62ad1c4..fc7e1d9 100644 (file)
@@ -1,6 +1,6 @@
 #
 # This file is part of DisOrder.
-# Copyright (C) 2004-2008 Richard Kettlewell
+# Copyright (C) 2004-2009 Richard Kettlewell
 #
 # 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
 sbin_PROGRAMS=disorderd disorder-deadlock disorder-rescan disorder-dump \
              disorder-speaker disorder-decode disorder-normalize \
              disorder-stats disorder-dbupgrade disorder-choose
-noinst_PROGRAMS=trackname
+noinst_PROGRAMS=trackname endian
 
 AM_CPPFLAGS=-I${top_srcdir}/lib -I../lib
 
 disorderd_SOURCES=disorderd.c api.c api-server.c daemonize.c play.c    \
        server.c server-queue.c queue-ops.c state.c plugin.c            \
-       schedule.c exports.c ../lib/memgc.c disorder-server.h
+       schedule.c dbparams.c background.c \
+       exports.c ../lib/memgc.c disorder-server.h
 disorderd_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
        $(LIBPCRE) $(LIBDB) $(LIBAO) $(LIBGC) $(LIBGCRYPT) $(LIBICONV) \
        $(LIBASOUND) $(COREAUDIO)
@@ -37,14 +38,10 @@ disorder_deadlock_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
        $(LIBDB) $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT)
 disorder_deadlock_DEPENDENCIES=../lib/libdisorder.a
 
-disorder_speaker_SOURCES=speaker.c speaker.h \
-                        speaker-command.c \
-                        speaker-network.c \
-                        speaker-coreaudio.c \
-                        speaker-oss.c \
-                        speaker-alsa.c
+disorder_speaker_SOURCES=speaker.c
 disorder_speaker_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
-       $(LIBASOUND) $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT) $(COREAUDIO)
+       $(LIBASOUND) $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT) $(COREAUDIO) \
+       $(LIBPTHREAD)
 disorder_speaker_DEPENDENCIES=../lib/libdisorder.a
 
 disorder_decode_SOURCES=decode.c disorder-server.h
@@ -54,7 +51,7 @@ disorder_decode_DEPENDENCIES=../lib/libdisorder.a
 
 disorder_normalize_SOURCES=normalize.c disorder-server.h
 disorder_normalize_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
-       $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT)
+       $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT) $(LIBSAMPLERATE)
 disorder_normalize_DEPENDENCIES=../lib/libdisorder.a
 
 disorder_rescan_SOURCES=rescan.c plugin.c api.c api-server.c exports.c \
@@ -67,7 +64,7 @@ disorder_rescan_DEPENDENCIES=../lib/libdisorder.a
 disorder_choose_SOURCES=choose.c server-queue.c ../lib/memgc.c \
                        disorder-server.h
 disorder_choose_LDADD=$(LIBOBJS) ../lib/libdisorder.a   \
-       $(LIBDB) $(LIBGC) $(LIBPCRE) $(LIBGCRYPT)
+       $(LIBDB) $(LIBGC) $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT)
 disorder_choose_LDFLAGS=-export-dynamic
 disorder_choose_DEPENDENCIES=../lib/libdisorder.a
 
@@ -120,21 +117,34 @@ check-help: all
        ./disorder-choose --help > /dev/null
        ./disorder-choose --version > /dev/null
 
-# My sox doesn't know MP3 or FLAC unfortunately
-check-decode: disorder-decode disorder-normalize
-       echo "speaker_backend network" > config
-       echo "broadcast 127.255.255.255 discard" > config
+check-decode: check-wav check-flac check-mp3
+
+check-mp3: disorder-decode disorder-normalize
+       ./disorder-decode ${top_srcdir}/sounds/scratch.mp3 | \
+         ./disorder-normalize --config ${srcdir}/test-config > mp3ed.raw
+       cmp mp3ed.raw ${top_srcdir}/sounds/scratch-mp3.raw
+       rm -f mp3ed.raw
+
+# TODO ogg decoding comes out OK but different depending on platform, version
+# or something.  Makes it tricky to test!
+check-ogg: disorder-decode disorder-normalize
        ./disorder-decode ${top_srcdir}/sounds/scratch.ogg | \
-         ./disorder-normalize --config config > decoded.raw
-       oggdec -b 16 -e 1 -R -s 1 -o oggdec.raw ${top_srcdir}/sounds/scratch.ogg
-       cmp decoded.raw oggdec.raw
-       sox ${top_srcdir}/sounds/scratch.ogg scratch.wav
-       ./disorder-decode scratch.wav | \
-         ./disorder-normalize --config config > decoded.raw
-       ls -l *.raw
-       cmp decoded.raw oggdec.raw
-       rm -f scratch.wav config decoded.raw oggdec.raw
-
-EXTRA_DIST=README.dbversions
+         ./disorder-normalize --config ${srcdir}/test-config > ogged.raw
+       cmp ogged.raw ${top_srcdir}/sounds/scratch.raw
+       rm -f ogged.raw
+
+check-wav: disorder-decode disorder-normalize
+       ./disorder-decode ${top_srcdir}/sounds/scratch.wav | \
+         ./disorder-normalize --config ${srcdir}/test-config > waved.raw
+       cmp waved.raw ${top_srcdir}/sounds/scratch.raw
+       rm -rf waved.raw
+
+check-flac: disorder-decode disorder-normalize
+       ./disorder-decode ${top_srcdir}/sounds/scratch.flac | \
+         ./disorder-normalize --config ${srcdir}/test-config > flacced.raw
+       cmp flacced.raw ${top_srcdir}/sounds/scratch.raw
+       rm -f flacced.raw
+
+EXTRA_DIST=README.dbversions test-config
 
 CLEANFILES=*.gcda *.gcov *.gcno *.c.html index.html
diff --git a/server/api-client.c b/server/api-client.c
deleted file mode 100644 (file)
index 9321770..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * This file is part of DisOrder.
- * Copyright (C) 2004, 2007, 2008 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "common.h"
-
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <locale.h>
-
-#include "client.h"
-#include "mem.h"
-#include "log.h"
-#include "configuration.h"
-#include "disorder.h"
-#include "api-client.h"
-
-static disorder_client *c;
-
-disorder_client *disorder_get_client(void) {
-  if(!c)
-    if(!(c = disorder_new(0))) exit(EXIT_FAILURE);
-  return c;
-}
-
-int disorder_track_exists(const char *track) {
-  int result;
-
-  return disorder_exists(c, track, &result) ? 0 : result;
-}
-
-const char *disorder_track_get_data(const char *track, const char *key) {
-  char *value;
-
-  if(disorder_get(c, track, key, &value)) return 0;
-  return value;
-}
-
-int disorder_track_set_data(const char *track,
-                           const char *key,
-                           const char *value) {
-  if(value)
-    return disorder_set(c, track, key, value);
-  else
-    return disorder_unset(c, track, key);
-}
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-End:
-*/
index fe9686c..e973fbe 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004, 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2004, 2007, 2008, 2009 Richard Kettlewell
  *
  * 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
  */
 #include "disorder-server.h"
 
-/* shared implementation of vararg functions */
-#include "log-impl.h"
-#include "mem-impl.h"
-
 void *disorder_malloc(size_t n) {
   return xmalloc(n);
 }
@@ -60,6 +56,16 @@ int disorder_snprintf(char buffer[], size_t bufsize, const char *fmt, ...) {
   return n;
 }
 
+int disorder_asprintf(char **rp, const char *fmt, ...) {
+  va_list ap;
+  int n;
+
+  va_start(ap, fmt);
+  n = byte_vasprintf(rp, fmt, ap);
+  va_end(ap);
+  return n;
+}
+
 /*
 Local Variables:
 c-basic-offset:2
diff --git a/server/background.c b/server/background.c
new file mode 100644 (file)
index 0000000..4901047
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2004-2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file server/background.c
+ * @brief Background process support for playing tracks
+ */
+
+#include "disorder-server.h"
+
+/** @brief Fork the player or decoder for @p q
+ * @param ev Event loop
+ * @param player Pointer to player information
+ * @param q Track to play or decode
+ * @param child Function to run inside fork
+ * @param bgdata Passed to @c child()
+ *
+ * @c q->pl had better already be set.
+ */
+int play_background(ev_source *ev,
+                    const struct stringlist *player,
+                    struct queue_entry *q,
+                    play_background_child_fn *child,
+                    void *bgdata) {
+  int lfd;
+  struct pbgc_params params[1];
+
+  memset(params, 0, sizeof params);
+  /* Get the raw path.  This needs to be done outside the fork.  It's needed by
+   * the play-track callback which has to have the raw filename bytes we got
+   * from readdir() as well as the normalized unicode version of the track
+   * name.  (Emphasize 'normalized'; even if you use UTF-8 for your filenames,
+   * they might not be normalized and if they are they might not be normalized
+   * to the same canonical form as DisOrder uses.) */
+  params->rawpath = trackdb_rawpath(q->track);
+  /* Call the prefork function in the player module.  None of the built-in
+   * modules use this so it's not well tested, unfortunately. */
+  if(q->type & DISORDER_PLAYER_PREFORK)
+    if(!(q->data = play_prefork(q->pl, q->track))) {
+      disorder_error(0, "prefork function for %s failed", q->track);
+      return START_HARDFAIL;
+    }
+  /* Capture the player/decoder's stderr and feed it into our logs.
+   *
+   * Use the second arg as the tag if available (it's probably a command name),
+   * otherwise the module name. */
+  if(!isatty(2))
+    lfd = logfd(ev, player->s[2] ? player->s[2] : player->s[1]);
+  else
+    lfd = -1;
+  /* Parse player arguments */
+  int optc = player->n - 2;
+  const char **optv = (const char **)&player->s[2];
+  while(optc > 0 && optv[0][0] == '-') {
+    if(!strcmp(optv[0], "--")) {
+      ++optv;
+      --optc;
+      break;
+    }
+    if(!strcmp(optv[0], "--wait-for-device")
+       || !strncmp(optv[0], "--wait-for-device=", 18)) {
+      const char *waitdevice;
+      if((waitdevice = strchr(optv[0], '='))) {
+       params->waitdevice = waitdevice + 1;
+      } else
+        params->waitdevice = "";       /* use default */
+      ++optv;
+      --optc;
+    } else {
+      disorder_error(0, "unknown option %s", optv[0]);
+      return START_HARDFAIL;
+    }
+  }
+  params->argc = optc;
+  params->argv = optv;
+  /* Create the child process */
+  switch(q->pid = fork()) {
+  case 0:
+    /* Child of disorderd */
+    exitfn = _exit;
+    progname = "disorderd-fork";
+    ev_signal_atfork(ev);
+    signal(SIGPIPE, SIG_DFL);
+    /* Send our log output to DisOrder's logs */
+    if(lfd != -1) {
+      xdup2(lfd, 1);
+      xdup2(lfd, 2);
+      xclose(lfd);                     /* tidy up */
+    }
+    /* Create a new process group, ID = child PID */
+    setpgid(0, 0);
+    _exit(child(q, params, bgdata));
+  case -1:
+    /* Back in disorderd (child could not be created) */
+    disorder_error(errno, "error calling fork");
+    if(q->type & DISORDER_PLAYER_PREFORK)
+      play_cleanup(q->pl, q->data);    /* else would leak */
+    if(lfd != -1)
+      xclose(lfd);
+    return START_SOFTFAIL;
+  }
+  /* We don't need the child's end of the log pipe */
+  if(lfd != -1)
+    xclose(lfd);
+  /* Set the child's process group ID.
+   *
+   * But wait, didn't we already set it in the child?  Yes, but it's possible
+   * that we'll need to address it by process group ID before it gets that far,
+   * so we set it here too.  One or the other may fail but as long as one
+   * succeeds that's fine.
+   */
+  setpgid(q->pid, q->pid);
+  /* Ask the event loop to tell us when the child terminates */
+  D(("player subprocess ID %lu", (unsigned long)q->pid));
+  return START_OK;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index bc82475..405a95d 100644 (file)
@@ -92,12 +92,12 @@ static unsigned long compute_weight(const char *track,
                                     struct kvp *prefs) {
   const char *s;
   char **track_tags;
-  time_t last, now = time(0);
+  time_t last, now = xtime(0);
 
   /* Reject tracks not in any collection (race between edit config and
    * rescan) */
   if(!find_track_root(track)) {
-    info("found track not in any collection: %s", track);
+    disorder_info("found track not in any collection: %s", track);
     return 0;
   }
 
@@ -264,7 +264,7 @@ int main(int argc, char **argv) {
   
   set_progname(argv);
   mem_init();
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "hVc:dDSs", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
@@ -274,14 +274,14 @@ int main(int argc, char **argv) {
     case 'D': debugging = 0; break;
     case 'S': logsyslog = 0; break;
     case 's': logsyslog = 1; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   if(logsyslog) {
     openlog(progname, LOG_PID, LOG_DAEMON);
     log_default = &log_syslog;
   }
-  if(config_read(0)) fatal(0, "cannot read configuration");
+  if(config_read(0, NULL)) disorder_fatal(0, "cannot read configuration");
   /* Find out current queue/recent list */
   queue_read();
   recent_read();
@@ -290,10 +290,10 @@ int main(int argc, char **argv) {
   trackdb_open(TRACKDB_NO_UPGRADE|TRACKDB_READ_ONLY);
   global_tid = trackdb_begin_transaction();
   if((err = trackdb_get_global_tid("required-tags", global_tid, &tags)))
-    fatal(0, "error getting required-tags: %s", db_strerror(err));
+    disorder_fatal(0, "error getting required-tags: %s", db_strerror(err));
   required_tags = parsetags(tags);
   if((err = trackdb_get_global_tid("prohibited-tags", global_tid, &tags)))
-    fatal(0, "error getting prohibited-tags: %s", db_strerror(err));
+    disorder_fatal(0, "error getting prohibited-tags: %s", db_strerror(err));
   prohibited_tags = parsetags(tags);
   if(trackdb_scan(0, collect_tracks_callback, 0, global_tid)) {
     global_tid->abort(global_tid);
@@ -301,12 +301,12 @@ int main(int argc, char **argv) {
   }
   trackdb_commit_transaction(global_tid);
   trackdb_close();
-  trackdb_deinit();
+  trackdb_deinit(NULL);
   D(("ntracks=%ld total_weight=%lld", ntracks, total_weight));
   if(!total_weight)
-    fatal(0, "no tracks match random choice criteria");
+    disorder_fatal(0, "no tracks match random choice criteria");
   if(!winning)
-    fatal(0, "internal: failed to pick a track");
+    disorder_fatal(0, "internal: failed to pick a track");
   /* Pick a track */
   xprintf("%s", winning);
   xfclose(stdout);
index c86154b..960b4ad 100644 (file)
@@ -41,7 +41,7 @@ void daemonize(const char *tag, int fac, const char *pidfile) {
    * /dev/null) */
   do {
     if((dn = open("/dev/null", O_RDWR, 0)) < 0)
-      fatal(errno, "error opening /dev/null");
+      disorder_fatal(errno, "error opening /dev/null");
   } while(dn < 3);
   pid = xfork();
   if(pid) {
@@ -50,14 +50,16 @@ void daemonize(const char *tag, int fac, const char *pidfile) {
     exitfn = _exit;
     while((r = waitpid(pid, &w, 0)) == -1 && errno == EINTR)
       ;
-    if(r < 0) fatal(errno, "error calling waitpid");
-    if(w) error(0, "subprocess exited with wait status %#x", (unsigned)w);
+    if(r < 0) disorder_fatal(errno, "error calling waitpid");
+    if(w)
+      disorder_error(0, "subprocess exited with wait status %#x", (unsigned)w);
     _exit(0);
   }
   /* First child process.  This will be the session leader, and will
    * be transient. */
   D(("first child pid=%lu", (unsigned long)getpid()));
-  if(setsid() < 0) fatal(errno, "error calling setsid");
+  if(setsid() < 0)
+    disorder_fatal(errno, "error calling setsid");
   /* we'll log to syslog */
   openlog(tag, LOG_PID, fac);
   log_default = &log_syslog;
@@ -75,7 +77,7 @@ void daemonize(const char *tag, int fac, const char *pidfile) {
     if(!(fp = fopen(pidfile, "w"))
        || fprintf(fp, "%lu\n", (unsigned long)getpid()) < 0
        || fclose(fp) < 0)
-      fatal(errno, "error creating %s", pidfile);
+      disorder_fatal(errno, "error creating %s", pidfile);
   }
 }
 
diff --git a/server/dbparams.c b/server/dbparams.c
new file mode 100644 (file)
index 0000000..4ebd248
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file server/dbparams.c
+ * @brief Parameters affecting the database
+ *
+ * Rescan can regenerate aliases and the search and tag databases but
+ * we rather assume that they are either empty or good.  Therefore we
+ * need to store anything that can affect these values and erase them
+ * if they change.
+ *
+ * The solution is a global pref _dbparams which contains the hash of
+ * the alias, stopword and namepart data.
+ */
+#include "disorder-server.h"
+#include <gcrypt.h>
+
+static int dbparams_cleanup(DB_TXN *tid);
+static void h_write_string(gcrypt_hash_handle h,
+                           const char *s);
+static char *compute_dbparams(void);
+
+/** @brief Check whether database parameters have changed
+ *
+ * If the database parameters have changed then deletes the search and
+ * tag database contents and all aliases.  The subsequent rescan will
+ * regenerate them.
+ */
+void dbparams_check(void) {
+  const char *newparams = compute_dbparams();
+  const char *oldparams = trackdb_get_global("_dbparams");
+
+  // If the parameters match, return straight away
+  if(oldparams && !strcmp(newparams, oldparams))
+    return;
+  // Log what we're going to do
+  for(;;) {
+    DB_TXN *tid;
+    if(oldparams)
+      disorder_info("database parameter string changed from %s to %s - removing old data",
+           oldparams, newparams);
+    else {
+      disorder_info("new database parameter string %s - removing old data",
+                    newparams);
+      /* This is a slightly annoying case; the global pref wasn't present.  In
+       * practice this is almost certainly either an upgrade (with no change to
+       * any relevant parameters) or a new installation (with no tracks).
+       *
+       * The new installation case doesn't matter much; clearing an empty
+       * search database and iterating over a likewise track database won't
+       * take long.
+       *
+       * However for upgrade this will throw away a lot of data and laboriously
+       * regenerate it, which is rather a shame.
+       */
+    }
+    tid = trackdb_begin_transaction();
+    int err = dbparams_cleanup(tid);
+    if(!err)
+      err = trackdb_set_global_tid("_dbparams", newparams, tid);
+    switch(err) {
+    case 0:
+      trackdb_commit_transaction(tid);
+      disorder_info("removed old data OK, will regenerate on rescan");
+      return;
+    case DB_LOCK_DEADLOCK:
+      /* Deadlocked, try again */
+      trackdb_abort_transaction(tid);
+      break;
+    default:
+      disorder_fatal(0, "error updating database: %s", db_strerror(err));
+    }
+  }
+}
+
+/** @brief Clean up databases */
+static int dbparams_cleanup(DB_TXN *tid) {
+  int err;
+  u_int32_t count;
+
+  /* We'll regenerate search.db based on the new set of stopwords */
+  if((err = trackdb_searchdb->truncate(trackdb_searchdb, tid, &count, 0))) {
+    disorder_error(err, "truncating search.db: %s", db_strerror(err));
+    return err;
+  }
+  /* We'll regenerate aliases based on the new alias/namepart settings, so
+   * delete all the alias records currently present
+   *
+   * TODO this looks suspiciously similar to part of dump.c
+   */
+  DBC *cursor;
+  DBT k, d;
+  cursor = trackdb_opencursor(trackdb_tracksdb, tid);
+  if((err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
+                          DB_FIRST)) == DB_LOCK_DEADLOCK) {
+    disorder_error(0, "cursor->c_get: %s", db_strerror(err));
+    goto done;
+  }
+  while(err == 0) {
+    struct kvp *data = kvp_urldecode(d.data, d.size);
+    if(kvp_get(data, "_alias_for")) {
+      if((err = cursor->c_del(cursor, 0))) {
+        disorder_error(0, "cursor->c_del: %s", db_strerror(err));
+        goto done;
+      }
+    }
+    err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d), DB_NEXT);
+  }
+  if(err == DB_LOCK_DEADLOCK) {
+    disorder_error(0, "cursor operation: %s", db_strerror(err));
+    goto done;
+  }
+  if(err != DB_NOTFOUND) 
+    disorder_fatal(0, "cursor->c_get: %s", db_strerror(err));
+  err = 0;
+done:
+  if(trackdb_closecursor(cursor) && !err) err = DB_LOCK_DEADLOCK;
+  return err;
+}
+
+/** @brief Write a string into a gcrypt hash function
+ * @param h Hash handle
+ * @param s String to write
+ *
+ * The 0 terminator is included in the byte written.
+ */
+static void h_write_string(gcrypt_hash_handle h,
+                           const char *s) {
+  gcry_md_write(h, s, strlen(s) + 1);
+}
+
+/** @brief Compute database parameters hash
+ * @return Opaque string encapsulating database parameters
+ */
+static char *compute_dbparams(void) {
+  gcry_error_t e;
+  gcrypt_hash_handle h;
+
+  if((e = gcry_md_open(&h, GCRY_MD_SHA256, 0)))
+    disorder_fatal(0, "gcry_md_open: %s", gcry_strerror(e));
+  h_write_string(h, "alias");
+  h_write_string(h, config->alias);
+  for(int n = 0; n < config->stopword.n; ++n) {
+    h_write_string(h, "stopword");
+    h_write_string(h, config->stopword.s[n]);
+  }
+  for(int n = 0; n < config->namepart.n; ++n) {
+    h_write_string(h, "namepart");
+    h_write_string(h, config->namepart.s[n].part);
+    h_write_string(h, config->namepart.s[n].res);
+    h_write_string(h, config->namepart.s[n].replace);
+    h_write_string(h, config->namepart.s[n].context);
+    char buffer[64];
+    snprintf(buffer, sizeof buffer, "%u", config->namepart.s[n].reflags);
+    h_write_string(h, buffer);
+  }
+  char *result;
+  byte_xasprintf(&result, "dbparams-0-sha256:%s", 
+                 hex(gcry_md_read(h, GCRY_MD_SHA256), 
+                     gcry_md_get_algo_dlen(GCRY_MD_SHA256)));
+  gcry_md_close(h);
+  return result;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index bd08eed..18e6aa1 100644 (file)
@@ -92,24 +92,24 @@ static int scan_core(const char *name, DB *db,
       break;
     ++count;
     if(count % 1000 == 0)
-      info("scanning %s, %ld so far", name, count);
+      disorder_info("scanning %s, %ld so far", name, count);
   }
   if(err && err != DB_NOTFOUND && err != DB_LOCK_DEADLOCK)
-    fatal(0, "%s: error scanning database: %s", name, db_strerror(err));
+    disorder_fatal(0, "%s: error scanning database: %s", name, db_strerror(err));
   r = (err == DB_LOCK_DEADLOCK ? err : 0);
   if((err = c->c_close(c)))
-    fatal(0, "%s: error closing cursor: %s", name, db_strerror(err));
-  info("%s: %ld entries scanned", name, count);
+    disorder_fatal(0, "%s: error closing cursor: %s", name, db_strerror(err));
+  disorder_info("%s: %ld entries scanned", name, count);
   if(values_normalized || values_already_ok)
-    info("%s: %ld values converted, %ld already ok", name,
-         values_normalized, values_already_ok);
+    disorder_info("%s: %ld values converted, %ld already ok", name,
+                  values_normalized, values_already_ok);
   if(keys_normalized || keys_already_ok)
-    info("%s: %ld keys converted, %ld already OK", name,
-         keys_normalized, keys_already_ok);
+    disorder_info("%s: %ld keys converted, %ld already OK", name,
+                  keys_normalized, keys_already_ok);
   if(aliases_removed)
-    info("%s: %ld aliases removed", name, aliases_removed);
+    disorder_info("%s: %ld aliases removed", name, aliases_removed);
   if(renoticed)
-    info("%s: %ld tracks re-noticed", name, renoticed);
+    disorder_info("%s: %ld tracks re-noticed", name, renoticed);
   return r;
 }
 
@@ -121,13 +121,13 @@ static int scan_core(const char *name, DB *db,
 static void scandb(const char *name, DB *db,
                    int (*callback)(const char *name, DB *db, DBC *c,
                                    DBT *k, DBT *d)) {
-  info("scanning %s", name);
+  disorder_info("scanning %s", name);
   for(;;) {
     global_tid = trackdb_begin_transaction();
     if(scan_core(name, db, callback)) {
       trackdb_abort_transaction(global_tid);
       global_tid = 0;
-      error(0, "detected deadlock, restarting scan");
+      disorder_error(0, "detected deadlock, restarting scan");
       continue;
     } else {
       trackdb_commit_transaction(global_tid);
@@ -146,7 +146,7 @@ static void truncate_database(const char *name, DB *db) {
     err = db->truncate(db, 0, &count, DB_AUTO_COMMIT);
   } while(err == DB_LOCK_DEADLOCK);
   if(err)
-    fatal(0, "error truncating %s: %s", name, db_strerror(err));
+    disorder_fatal(0, "error truncating %s: %s", name, db_strerror(err));
 }
 
 /* scan callbacks */
@@ -162,22 +162,22 @@ static int normalize_keys(const char *name, DB *db, DBC *c,
   if(!knfc) {
     switch(badkey) {
     case BADKEY_WARN:
-      error(0, "%s: invalid key: %.*s", name,
-            (int)k->size, (const char *)k->data);
+      disorder_error(0, "%s: invalid key: %.*s", name,
+                     (int)k->size, (const char *)k->data);
       break;
     case BADKEY_DELETE:
-      error(0, "%s: deleting invalid key: %.*s", name,
-            (int)k->size, (const char *)k->data);
+      disorder_error(0, "%s: deleting invalid key: %.*s", name,
+                     (int)k->size, (const char *)k->data);
       if((err = c->c_del(c, 0))) {
         if(err != DB_LOCK_DEADLOCK)
-          fatal(0, "%s: error removing denormalized key: %s",
-                name, db_strerror(err));
+          disorder_fatal(0, "%s: error removing denormalized key: %s",
+                         name, db_strerror(err));
         return err;
       }
       break;
     case BADKEY_FAIL:
-      fatal(0, "%s: invalid key: %.*s", name,
-            (int)k->size, (const char *)k->data);
+      disorder_fatal(0, "%s: invalid key: %.*s", name,
+                     (int)k->size, (const char *)k->data);
     }
     return 0;
   }
@@ -189,15 +189,16 @@ static int normalize_keys(const char *name, DB *db, DBC *c,
   /* To rename the key we must delete the old one and insert a new one */
   if((err = c->c_del(c, 0))) {
     if(err != DB_LOCK_DEADLOCK)
-      fatal(0, "%s: error removing denormalized key: %s",
-            name, db_strerror(err));
+      disorder_fatal(0, "%s: error removing denormalized key: %s",
+                     name, db_strerror(err));
     return err;
   }
   k->size = nknfc;
   k->data = knfc;
   if((err = db->put(db, global_tid, k, d, DB_NOOVERWRITE))) {
     if(err != DB_LOCK_DEADLOCK)
-      fatal(0, "%s: error storing normalized key: %s", name, db_strerror(err));
+      disorder_fatal(0, "%s: error storing normalized key: %s",
+                     name, db_strerror(err));
     return err;
   }
   ++keys_normalized;
@@ -214,8 +215,8 @@ static int normalize_values(const char *name, DB *db,
   /* Find the normalized form of the value */
   dnfc = utf8_compose_canon(d->data, d->size, &ndnfc);
   if(!dnfc)
-    fatal(0, "%s: cannot convert data to NFC: %.*s", name,
-          (int)d->size, (const char *)d->data);
+    disorder_fatal(0, "%s: cannot convert data to NFC: %.*s", name,
+                   (int)d->size, (const char *)d->data);
   /* If the key is already in NFC then do nothing */
   if(ndnfc == d->size && !memcmp(d->data, dnfc, ndnfc)) {
     ++values_already_ok;
@@ -225,7 +226,8 @@ static int normalize_values(const char *name, DB *db,
   d->data = dnfc;
   if((err = db->put(db, global_tid, k, d, 0))) {
     if(err != DB_LOCK_DEADLOCK)
-      fatal(0, "%s: error storing normalized data: %s", name, db_strerror(err));
+      disorder_fatal(0, "%s: error storing normalized data: %s",
+                     name, db_strerror(err));
     return err;
   }
   ++values_normalized;
@@ -245,8 +247,8 @@ static int renotice(const char *name, DB attribute((unused)) *db,
      * in the scan. */
     if(kvp_get(t, "_alias_for"))
       return 0;
-    fatal(0, "%s: no '_path' for %.*s", name,
-          (int)k->size, (const char *)k->data);
+    disorder_fatal(0, "%s: no '_path' for %.*s", name,
+                   (int)k->size, (const char *)k->data);
   }
   switch(err = trackdb_notice_tid(track, path, global_tid)) {
   case 0:
@@ -255,8 +257,8 @@ static int renotice(const char *name, DB attribute((unused)) *db,
   case DB_LOCK_DEADLOCK:
     return err;
   default:
-    fatal(0, "%s: unexpected return from trackdb_notice_tid: %s",
-          name, db_strerror(err));
+    disorder_fatal(0, "%s: unexpected return from trackdb_notice_tid: %s",
+                   name, db_strerror(err));
   }
 }
  
@@ -269,14 +271,14 @@ static int remove_aliases_normalize_keys(const char *name, DB *db, DBC *c,
     /* This is an alias.  We remove all the alias entries. */
     if((err = c->c_del(c, 0))) {
       if(err != DB_LOCK_DEADLOCK)
-        fatal(0, "%s: error removing alias: %s", name, db_strerror(err));
+        disorder_fatal(0, "%s: error removing alias: %s", name, db_strerror(err));
       return err;
     }
     ++aliases_removed;
     return 0;
   } else if(!kvp_get(t, "_path"))
-    error(0, "%s: %.*s has neither _alias_for nor _path", name,
-          (int)k->size, (const char *)k->data);
+    disorder_error(0, "%s: %.*s has neither _alias_for nor _path", name,
+                   (int)k->size, (const char *)k->data);
   return normalize_keys(name, db, c, k, d);
 }
 
@@ -288,16 +290,16 @@ static int remove_aliases_normalize_keys(const char *name, DB *db, DBC *c,
 static void upgrade(void) {
   char buf[32];
 
-  info("upgrading database to dbversion %ld", config->dbversion);
+  disorder_info("upgrading database to dbversion %ld", config->dbversion);
   /* Normalize keys and values as required.  We will also remove aliases as
    * they will be regenerated when we re-noticed the tracks. */
-  info("renormalizing keys");
+  disorder_info("renormalizing keys");
   scandb("tracks.db", trackdb_tracksdb, remove_aliases_normalize_keys);
   scandb("prefs.db", trackdb_prefsdb, normalize_keys);
   scandb("global.db", trackdb_globaldb, normalize_keys);
   scandb("noticed.db", trackdb_noticeddb, normalize_values);
   /* search.db and tags.db we will rebuild */
-  info("regenerating search database and aliases");
+  disorder_info("regenerating search database and aliases");
   truncate_database("search.db", trackdb_searchdb);
   truncate_database("tags.db", trackdb_tagsdb);
   /* Regenerate the search database and aliases */
@@ -305,7 +307,7 @@ static void upgrade(void) {
   /* Finally update the database version */
   snprintf(buf, sizeof buf, "%ld", config->dbversion);
   trackdb_set_global("_dbversion", buf, 0);
-  info("completed database upgrade");
+  disorder_info("completed database upgrade");
 }
 
 int main(int argc, char **argv) {
@@ -313,7 +315,7 @@ int main(int argc, char **argv) {
   
   set_progname(argv);
   mem_init();
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "hVc:dDSsxX", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
@@ -325,7 +327,7 @@ int main(int argc, char **argv) {
     case 's': logsyslog = 1; break;
     case 'x': badkey = BADKEY_DELETE; break;
     case 'X': badkey = BADKEY_FAIL; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   /* If stderr is a TTY then log there, otherwise to syslog. */
@@ -333,7 +335,7 @@ int main(int argc, char **argv) {
     openlog(progname, LOG_PID, LOG_DAEMON);
     log_default = &log_syslog;
   }
-  if(config_read(0)) fatal(0, "cannot read configuration");
+  if(config_read(0, NULL)) disorder_fatal(0, "cannot read configuration");
   /* Open the database */
   trackdb_init(TRACKDB_NO_RECOVER);
   trackdb_open(TRACKDB_OPEN_FOR_UPGRADE);
index 3c13351..72a82d4 100644 (file)
@@ -54,7 +54,7 @@ int main(int argc, char **argv) {
   int n, err, aborted, logsyslog = !isatty(2);
 
   set_progname(argv);
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "hVc:dDSs", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
@@ -64,28 +64,28 @@ int main(int argc, char **argv) {
     case 'D': debugging = 0; break;
     case 'S': logsyslog = 0; break;
     case 's': logsyslog = 1; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   if(logsyslog) {
     openlog(progname, LOG_PID, LOG_DAEMON);
     log_default = &log_syslog;
   }
-  if(config_read(0)) fatal(0, "cannot read configuration");
-  info("started");
+  if(config_read(0, NULL)) disorder_fatal(0, "cannot read configuration");
+  disorder_info("started");
   trackdb_init(TRACKDB_NO_RECOVER);
   while(getppid() != 1) {
     if((err = trackdb_env->lock_detect(trackdb_env,
                                       0,
                                       DB_LOCK_DEFAULT,
                                       &aborted)))
-      fatal(0, "trackdb_env->lock_detect: %s", db_strerror(err));
+      disorder_fatal(0, "trackdb_env->lock_detect: %s", db_strerror(err));
     if(aborted)
       D(("aborted %d lock requests", aborted));
     sleep(1);
   }
   /* if our parent goes away, it's time to stop */
-  info("stopped (parent terminated)");
+  disorder_info("stopped (parent terminated)");
   return 0;
 }
 
index ef999b3..11a0592 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder
- * Copyright (C) 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2007-2009 Richard Kettlewell
  *
  * 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
@@ -63,14 +63,14 @@ static int input_count;
 /** @brief Write an 8-bit word */
 static inline void output_8(int n) {
   if(putc(n, outputfp) < 0)
-    fatal(errno, "decoding %s: output error", path);
+    disorder_fatal(errno, "decoding %s: output error", path);
 }
 
 /** @brief Write a 16-bit word in bigendian format */
 static inline void output_16(uint16_t n) {
   if(putc(n >> 8, outputfp) < 0
      || putc(n, outputfp) < 0)
-    fatal(errno, "decoding %s: output error", path);
+    disorder_fatal(errno, "decoding %s: output error", path);
 }
 
 /** @brief Write a 24-bit word in bigendian format */
@@ -78,7 +78,7 @@ static inline void output_24(uint32_t n) {
   if(putc(n >> 16, outputfp) < 0
      || putc(n >> 8, outputfp) < 0
      || putc(n, outputfp) < 0)
-    fatal(errno, "decoding %s: output error", path);
+    disorder_fatal(errno, "decoding %s: output error", path);
 }
 
 /** @brief Write a 32-bit word in bigendian format */
@@ -87,7 +87,7 @@ static inline void output_32(uint32_t n) {
      || putc(n >> 16, outputfp) < 0
      || putc(n >> 8, outputfp) < 0
      || putc(n, outputfp) < 0)
-    fatal(errno, "decoding %s: output error", path);
+    disorder_fatal(errno, "decoding %s: output error", path);
 }
 
 /** @brief Write a block header
@@ -98,7 +98,7 @@ static inline void output_32(uint32_t n) {
  * @param endian @ref ENDIAN_BIG or @ref ENDIAN_LITTLE
  *
  * Checks that the sample format is a supported one (so other calls do not have
- * to) and calls fatal() on error.
+ * to) and calls disorder_fatal() on error.
  */
 static void output_header(int rate,
                          int channels,
@@ -108,18 +108,20 @@ static void output_header(int rate,
   struct stream_header header;
 
   if(bits <= 0 || bits % 8 || bits > 64)
-    fatal(0, "decoding %s: unsupported sample size %d bits", path, bits);
+    disorder_fatal(0, "decoding %s: unsupported sample size %d bits",
+                   path, bits);
   if(channels <= 0 || channels > 2)
-    fatal(0, "decoding %s: unsupported channel count %d", path, channels);
+    disorder_fatal(0, "decoding %s: unsupported channel count %d",
+                   path, channels);
   if(rate <= 0)
-    fatal(0, "decoding %s: nonsensical sample rate %dHz", path, rate);
+    disorder_fatal(0, "decoding %s: nonsensical sample rate %dHz", path, rate);
   header.rate = rate;
   header.bits = bits;
   header.channels = channels;
   header.endian = endian;
   header.nbytes = nbytes;
   if(fwrite(&header, sizeof header, 1, outputfp) < 1)
-    fatal(errno, "decoding %s: writing format header", path);
+    disorder_fatal(errno, "decoding %s: writing format header", path);
 }
 
 /** @brief Dithering state
@@ -239,7 +241,7 @@ static enum mad_flow mp3_input(void attribute((unused)) *data,
   /* Read new data */
   n = read(inputfd, input_buffer + remain, (sizeof input_buffer) - remain);
   if(n < 0)
-    fatal(errno, "reading from %s", path);
+    disorder_fatal(errno, "reading from %s", path);
   /* Compute total number of bytes available */
   input_count = remain + n;
   if(input_count)
@@ -256,8 +258,8 @@ static enum mad_flow mp3_error(void attribute((unused)) *data,
                               struct mad_frame attribute((unused)) *frame) {
   if(0)
     /* Just generates pointless verbosity l-( */
-    error(0, "decoding %s: %s (%#04x)",
-          path, mad_stream_errorstr(stream), stream->error);
+    disorder_error(0, "decoding %s: %s (%#04x)",
+                   path, mad_stream_errorstr(stream), stream->error);
   return MAD_FLOW_CONTINUE;
 }
 
@@ -266,7 +268,7 @@ static void decode_mp3(void) {
   struct mad_decoder mad[1];
 
   if((inputfd = open(path, O_RDONLY)) < 0)
-    fatal(errno, "opening %s", path);
+    disorder_fatal(errno, "opening %s", path);
   mad_decoder_init(mad, 0/*data*/, mp3_input, 0/*header*/, 0/*filter*/,
                   mp3_output, mp3_error, 0/*message*/);
   if(mad_decoder_run(mad, MAD_DECODER_MODE_SYNC))
@@ -284,22 +286,22 @@ static void decode_ogg(void) {
   vorbis_info *vi;
 
   if(!(fp = fopen(path, "rb")))
-    fatal(errno, "cannot open %s", path);
+    disorder_fatal(errno, "cannot open %s", path);
   /* There doesn't seem to be any standard function for mapping the error codes
    * to strings l-( */
   if((err = ov_open(fp, vf, 0/*initial*/, 0/*ibytes*/)))
-    fatal(0, "ov_fopen %s: %d", path, err);
+    disorder_fatal(0, "ov_fopen %s: %d", path, err);
   if(!(vi = ov_info(vf, 0/*link*/)))
-    fatal(0, "ov_info %s: failed", path);
+    disorder_fatal(0, "ov_info %s: failed", path);
   while((n = ov_read(vf, input_buffer, sizeof input_buffer, 1/*bigendianp*/,
                      2/*bytes/word*/, 1/*signed*/, &bitstream))) {
     if(n < 0)
-      fatal(0, "ov_read %s: %ld", path, n);
+      disorder_fatal(0, "ov_read %s: %ld", path, n);
     if(bitstream > 0)
-      fatal(0, "only single-bitstream ogg files are supported");
+      disorder_fatal(0, "only single-bitstream ogg files are supported");
     output_header(vi->rate, vi->channels, 16/*bits*/, n, ENDIAN_BIG);
     if(fwrite(input_buffer, 1, n, outputfp) < (size_t)n)
-      fatal(errno, "decoding %s: writing sample data", path);
+      disorder_fatal(errno, "decoding %s: writing sample data", path);
   }
 }
 
@@ -309,7 +311,7 @@ static int wav_write(struct wavfile attribute((unused)) *f,
                      size_t nbytes,
                      void attribute((unused)) *u) {
   if(fwrite(data, 1, nbytes, outputfp) < nbytes)
-    fatal(errno, "decoding %s: writing sample data", path);
+    disorder_fatal(errno, "decoding %s: writing sample data", path);
   return 0;
 }
 
@@ -319,10 +321,10 @@ static void decode_wav(void) {
   int err;
 
   if((err = wav_init(f, path)))
-    fatal(err, "opening %s", path);
+    disorder_fatal(err, "opening %s", path);
   output_header(f->rate, f->channels, f->bits, f->datasize, ENDIAN_LITTLE);
   if((err = wav_data(f, wav_write, 0)))
-    fatal(err, "error decoding %s", path);
+    disorder_fatal(err, "error decoding %s", path);
 }
 
 /** @brief Metadata callback for FLAC decoder
@@ -338,8 +340,8 @@ static void flac_metadata(const FLAC__FileDecoder attribute((unused)) *decoder,
 static void flac_error(const FLAC__FileDecoder attribute((unused)) *decoder,
                       FLAC__StreamDecoderErrorStatus status,
                       void attribute((unused)) *client_data) {
-  fatal(0, "error decoding %s: %s", path,
-        FLAC__StreamDecoderErrorStatusString[status]);
+  disorder_fatal(0, "error decoding %s: %s", path,
+                 FLAC__StreamDecoderErrorStatusString[status]);
 }
 
 /** @brief Write callback for FLAC decoder */
@@ -377,23 +379,30 @@ static void decode_flac(void) {
   FLAC__FileDecoderState fs;
 
   if(!(fd = FLAC__file_decoder_new()))
-    fatal(0, "FLAC__file_decoder_new failed");
+    disorder_fatal(0, "FLAC__file_decoder_new failed");
   if(!(FLAC__file_decoder_set_filename(fd, path)))
-    fatal(0, "FLAC__file_set_filename failed");
+    disorder_fatal(0, "FLAC__file_set_filename failed");
   FLAC__file_decoder_set_metadata_callback(fd, flac_metadata);
   FLAC__file_decoder_set_error_callback(fd, flac_error);
   FLAC__file_decoder_set_write_callback(fd, flac_write);
   if((fs = FLAC__file_decoder_init(fd)))
-    fatal(0, "FLAC__file_decoder_init: %s", FLAC__FileDecoderStateString[fs]);
+    disorder_fatal(0, "FLAC__file_decoder_init: %s", FLAC__FileDecoderStateString[fs]);
   FLAC__file_decoder_process_until_end_of_file(fd);
 #else
-  FLAC__StreamDecoder *sd = 0;
+  FLAC__StreamDecoder *sd = FLAC__stream_decoder_new();
   FLAC__StreamDecoderInitStatus is;
 
+  if (!sd)
+    disorder_fatal(0, "FLAC__stream_decoder_new failed");
+
   if((is = FLAC__stream_decoder_init_file(sd, path, flac_write, flac_metadata,
                                           flac_error, 0)))
-    fatal(0, "FLAC__stream_decoder_init_file %s: %s",
-          path, FLAC__StreamDecoderInitStatusString[is]);
+    disorder_fatal(0, "FLAC__stream_decoder_init_file %s: %s",
+                   path, FLAC__StreamDecoderInitStatusString[is]);
+
+  FLAC__stream_decoder_process_until_end_of_stream(sd);
+  FLAC__stream_decoder_finish(sd);
+  FLAC__stream_decoder_delete(sd);
 #endif
 }
 
@@ -435,21 +444,21 @@ int main(int argc, char **argv) {
   const char *e;
 
   set_progname(argv);
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "calling setlocale");
+  if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "calling setlocale");
   while((n = getopt_long(argc, argv, "hV", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
     case 'V': version("disorder-decode");
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   if(optind >= argc)
-    fatal(0, "missing filename");
+    disorder_fatal(0, "missing filename");
   if(optind + 1 < argc)
-    fatal(0, "excess arguments");
+    disorder_fatal(0, "excess arguments");
   if((e = getenv("DISORDER_RAW_FD"))) {
     if(!(outputfp = fdopen(atoi(e), "wb")))
-      fatal(errno, "fdopen");
+      disorder_fatal(errno, "fdopen");
   } else
     outputfp = stdout;
   path = argv[optind];
@@ -459,7 +468,7 @@ int main(int argc, char **argv) {
       ++n)
     ;
   if(!decoders[n].pattern)
-    fatal(0, "cannot determine file type for %s", path);
+    disorder_fatal(0, "cannot determine file type for %s", path);
   decoders[n].decode();
   xfclose(outputfp);
   return 0;
index 4eb58e4..e8f4dab 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder
- * Copyright (C) 2008 Richard Kettlewell
+ * Copyright (C) 2008, 2009 Richard Kettlewell
  *
  * 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
@@ -67,7 +67,6 @@
 #include "logfd.h"
 #include "mem.h"
 #include "mime.h"
-#include "mixer.h"
 #include "printf.h"
 #include "queue.h"
 #include "random.h"
 #include "trackdb-int.h"
 #include "trackdb.h"
 #include "trackname.h"
+#include "uaudio.h"
 #include "unicode.h"
 #include "user.h"
 #include "vector.h"
 #include "version.h"
 #include "wstat.h"
 
+extern const struct uaudio *api;
+
 void daemonize(const char *tag, int fac, const char *pidfile);
 /* Go into background.  Send stdout/stderr to syslog.
  * If @pri@ is non-null, it should be "facility.level"
@@ -97,8 +99,18 @@ void daemonize(const char *tag, int fac, const char *pidfile);
 void quit(ev_source *ev) attribute((noreturn));
 /* terminate the daemon */
 
-int reconfigure(ev_source *ev, int reload);
-/* reconfigure.  If @reload@ is nonzero, update the configuration. */
+int reconfigure(ev_source *ev, unsigned flags);
+/* reconfigure */
+
+void reset_sockets(ev_source *ev);
+
+/** @brief Set when starting server */
+#define RECONFIGURE_FIRST 0x0001
+
+/** @brief Set when reloading after SIGHUP etc */
+#define RECONFIGURE_RELOADING 0x0002
+
+void dbparams_check(void);
 
 extern struct queue_entry qhead;
 /* queue of things yet to be played.  the head will be played
@@ -122,10 +134,13 @@ void recent_write(void);
 /* write the recently played list out.  Calls @fatal@ on error. */
 
 struct queue_entry *queue_add(const char *track, const char *submitter,
-                             int where, enum track_origin origin);
+                             int where, const char *target,
+                              enum track_origin origin);
 #define WHERE_START 0                  /* Add to head of queue */
 #define WHERE_END 1                    /* Add to end of queue */
 #define WHERE_BEFORE_RANDOM 2          /* End, or before random track */
+#define WHERE_AFTER 3                   /* After the target */
+#define WHERE_NOWHERE 4                 /* Don't add to queue at all */
 /* add an entry to the queue.  Return a pointer to the new entry. */
 
 void queue_remove(struct queue_entry *q, const char *who);
@@ -322,6 +337,41 @@ int play_pause(const struct plugin *pl, long *playedp, void *data);
 void play_resume(const struct plugin *pl, void *data);
 /* Resume track. */
 
+/* background process support *************************************************/
+
+/** @brief Child process parameters */
+struct pbgc_params {
+  /** @brief Length of player command */
+  int argc;
+  /** @brief Player command */
+  const char **argv;
+  /** @brief Device to wait for or NULL */
+  const char *waitdevice;
+  /** @brief Raw track name */
+  const char *rawpath;
+};
+
+/** @brief Callback to play or prepare a track
+ * @param q Track to play or decode
+ * @param bgdata User data pointer
+ * @return Exit code
+ */
+typedef int play_background_child_fn(struct queue_entry *q,
+                                     const struct pbgc_params *params,
+                                     void *bgdata);
+
+int play_background(ev_source *ev,
+                    const struct stringlist *player,
+                    struct queue_entry *q,
+                    play_background_child_fn *child,
+                    void *bgdata);
+
+/* Return values from start(),  prepare() and play_background() */
+
+#define START_OK 0        /**< @brief Succeeded. */
+#define START_HARDFAIL 1   /**< @brief Track is broken. */
+#define START_SOFTFAIL 2   /**< @brief Track OK, system (temporarily?) broken */
+
 #endif /* DISORDER_SERVER_H */
 
 /*
index 2342c3c..14b459f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004-2008 Richard Kettlewell
+ * Copyright (C) 2004-2009 Richard Kettlewell
  *
  * 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
@@ -57,8 +57,8 @@ static void help(void) {
 static int handle_sighup(ev_source attribute((unused)) *ev_,
                         int attribute((unused)) sig,
                         void attribute((unused)) *u) {
-  info("received SIGHUP");
-  reconfigure(ev, 1);
+  disorder_info("received SIGHUP");
+  reconfigure(ev, RECONFIGURE_RELOADING);
   return 0;
 }
 
@@ -67,14 +67,14 @@ static int handle_sighup(ev_source attribute((unused)) *ev_,
 static int handle_sigint(ev_source attribute((unused)) *ev_,
                         int attribute((unused)) sig,
                         void attribute((unused)) *u) {
-  info("received SIGINT");
+  disorder_info("received SIGINT");
   quit(ev);
 }
 
 static int handle_sigterm(ev_source attribute((unused)) *ev_,
                          int attribute((unused)) sig,
                          void attribute((unused)) *u) {
-  info("received SIGTERM");
+  disorder_info("received SIGTERM");
   quit(ev);
 }
 
@@ -132,7 +132,8 @@ static void periodic_volume_check(ev_source attribute((unused)) *ev_) {
   int l, r;
   char lb[32], rb[32];
 
-  if(!mixer_control(-1/*as configured*/, &l, &r, 0)) {
+  if(api && api->get_volume) {
+    api->get_volume(&l, &r);
     if(l != volume_left || r != volume_right) {
       volume_left = l;
       volume_right = r;
@@ -158,7 +159,7 @@ static void fix_path(void) {
   /* static or libgc collects it! */
 
   if(!path)
-    error(0, "PATH is not set at all!");
+    disorder_error(0, "PATH is not set at all!");
 
   if(*finkbindir && strcmp(finkbindir, "/"))
     /* We appear to be a finkized mac; include fink on the path in case the
@@ -168,7 +169,7 @@ static void fix_path(void) {
   else
     byte_xasprintf(&newpath, "PATH=%s:%s:%s", path, bindir, sbindir);
   putenv(newpath);
-  info("%s", newpath); 
+  disorder_info("%s", newpath); 
 }
 
 int main(int argc, char **argv) {
@@ -178,7 +179,8 @@ int main(int argc, char **argv) {
 
   set_progname(argv);
   mem_init();
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_CTYPE, ""))
+    disorder_fatal(errno, "error calling setlocale");
   /* garbage-collect PCRE's memory */
   pcre_malloc = xmalloc;
   pcre_free = xfree;
@@ -192,7 +194,7 @@ int main(int argc, char **argv) {
     case 'P': pidfile = optarg; break;
     case 's': logsyslog = 1; break;
     case 'w': wideopen = 1; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   /* go into background if necessary */
@@ -204,29 +206,33 @@ int main(int argc, char **argv) {
     openlog(progname, LOG_PID, LOG_DAEMON);
     log_default = &log_syslog;
   }
-  info("process ID %lu", (unsigned long)getpid());
+  disorder_info("process ID %lu", (unsigned long)getpid());
   fix_path();
-  srand(time(0));                      /* don't start the same every time */
+  srand(xtime(0));                     /* don't start the same every time */
   /* gcrypt initialization */
+  if(!gcry_check_version(NULL))
+    disorder_fatal(0, "gcry_check_version failed");
   gcry_control(GCRYCTL_INIT_SECMEM, 1);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
   /* make sure we can't have more than FD_SETSIZE files open (event.c does
    * check but this provides an additional line of defence) */
   if(getrlimit(RLIMIT_NOFILE, rl) < 0)
-    fatal(errno, "getrlimit RLIMIT_NOFILE");
+    disorder_fatal(errno, "getrlimit RLIMIT_NOFILE");
   if(rl->rlim_cur > FD_SETSIZE) {
     rl->rlim_cur = FD_SETSIZE;
     if(setrlimit(RLIMIT_NOFILE, rl) < 0)
-      fatal(errno, "setrlimit to reduce RLIMIT_NOFILE to %lu",
-            (unsigned long)rl->rlim_cur);
-    info("set RLIM_NOFILE to %lu", (unsigned long)rl->rlim_cur);
+      disorder_fatal(errno, "setrlimit to reduce RLIMIT_NOFILE to %lu",
+                    (unsigned long)rl->rlim_cur);
+    disorder_info("set RLIM_NOFILE to %lu", (unsigned long)rl->rlim_cur);
   } else
-    info("RLIM_NOFILE is %lu", (unsigned long)rl->rlim_cur);
+    disorder_info("RLIM_NOFILE is %lu", (unsigned long)rl->rlim_cur);
   /* create event loop */
   ev = ev_new();
-  if(ev_child_setup(ev)) fatal(0, "ev_child_setup failed");
+  if(ev_child_setup(ev)) disorder_fatal(0, "ev_child_setup failed");
   /* read config */
-  if(config_read(1))
-    fatal(0, "cannot read configuration");
+  config_uaudio_apis = uaudio_apis;
+  if(config_read(1,  NULL))
+    disorder_fatal(0, "cannot read configuration");
   /* make sure the home directory exists and has suitable permissions */
   make_home();
   /* Start the speaker process (as root! - so it can choose its nice value) */
@@ -237,37 +243,53 @@ int main(int argc, char **argv) {
   /* change user */
   become_mortal();
   /* make sure we're not root, whatever the config says */
-  if(getuid() == 0 || geteuid() == 0) fatal(0, "do not run as root");
+  if(getuid() == 0 || geteuid() == 0)
+    disorder_fatal(0, "do not run as root");
   /* open a lockfile - we only want one copy of the server to run at once. */
-  if(config->lock) {
+  if(1) {
     const char *lockfile;
     int lockfd;
     struct flock lock;
 
     lockfile = config_get_file("lock");
     if((lockfd = open(lockfile, O_RDWR|O_CREAT, 0600)) < 0)
-      fatal(errno, "error opening %s", lockfile);
+      disorder_fatal(errno, "error opening %s", lockfile);
     cloexec(lockfd);
     memset(&lock, 0, sizeof lock);
     lock.l_type = F_WRLCK;
     lock.l_whence = SEEK_SET;
     if(fcntl(lockfd, F_SETLK, &lock) < 0)
-      fatal(errno, "error locking %s", lockfile);
+      disorder_fatal(errno, "error locking %s", lockfile);
   }
   /* initialize database environment */
   trackdb_init(TRACKDB_NORMAL_RECOVER|TRACKDB_MAY_CREATE);
   trackdb_master(ev);
-  /* install new config (calls trackdb_open()) */
-  reconfigure(ev, 0);
+  /* install new config; don't create socket */
+  if(reconfigure(ev, RECONFIGURE_FIRST))
+    disorder_fatal(0, "failed to read configuration");
+  /* Open the database */
+  trackdb_open(TRACKDB_CAN_UPGRADE);
+  /* load the queue and recently-played list */
+  queue_read();
+  recent_read();
+  /* Arrange timeouts for schedule actions */
+  schedule_init(ev);
   /* pull in old users */
   trackdb_old_users();
   /* create a root login */
   trackdb_create_root();
+  /* create sockets */
+  reset_sockets(ev);
+  /* check for change to database parameters */
+  dbparams_check();
   /* re-read config if we receive a SIGHUP */
-  if(ev_signal(ev, SIGHUP, handle_sighup, 0)) fatal(0, "ev_signal failed");
+  if(ev_signal(ev, SIGHUP, handle_sighup, 0))
+    disorder_fatal(0, "ev_signal failed");
   /* exit on SIGINT/SIGTERM */
-  if(ev_signal(ev, SIGINT, handle_sigint, 0)) fatal(0, "ev_signal failed");
-  if(ev_signal(ev, SIGTERM, handle_sigterm, 0)) fatal(0, "ev_signal failed");
+  if(ev_signal(ev, SIGINT, handle_sigint, 0))
+    disorder_fatal(0, "ev_signal failed");
+  if(ev_signal(ev, SIGTERM, handle_sigterm, 0))
+    disorder_fatal(0, "ev_signal failed");
   /* ignore SIGPIPE */
   signal(SIGPIPE, SIG_IGN);
   /* Rescan immediately and then daily */
@@ -283,7 +305,7 @@ int main(int argc, char **argv) {
   /* enter the event loop */
   n = ev_run(ev);
   /* if we exit the event loop, something must have gone wrong */
-  fatal(errno, "ev_run returned %d", n);
+  disorder_fatal(errno, "ev_run returned %d", n);
 }
 
 /*
index e08b32f..cdc3a44 100644 (file)
@@ -29,8 +29,6 @@ static const struct option options[] = {
   { "debug", no_argument, 0, 'D' },
   { "recover", no_argument, 0, 'r' },
   { "recover-fatal", no_argument, 0, 'R' },
-  { "trackdb", no_argument, 0, 't' },
-  { "searchdb", no_argument, 0, 's' },
   { "recompute-aliases", no_argument, 0, 'a' },
   { "remove-pathless", no_argument, 0, 'P' },
   { 0, 0, 0, 0 }
@@ -55,148 +53,98 @@ static void help(void) {
   exit(0);
 }
 
+/** @brief Dump one record
+ * @param s Output stream
+ * @param tag Tag for error messages
+ * @param letter Prefix leter for dumped record
+ * @param dbname Database name
+ * @param db Database handle
+ * @param tid Transaction handle
+ * @return 0 or @c DB_LOCK_DEADLOCK
+ */
+static int dump_one(struct sink *s,
+                    const char *tag,
+                    int letter,
+                    const char *dbname,
+                    DB *db,
+                    DB_TXN *tid) {
+  int err;
+  DBC *cursor;
+  DBT k, d;
+
+  /* dump the preferences */
+  cursor = trackdb_opencursor(db, tid);
+  err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
+                      DB_FIRST);
+  while(err == 0) {
+    if(sink_writec(s, letter) < 0
+       || urlencode(s, k.data, k.size)
+       || sink_writec(s, '\n') < 0
+       || urlencode(s, d.data, d.size)
+       || sink_writec(s, '\n') < 0)
+      disorder_fatal(errno, "error writing to %s", tag);
+    err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
+                        DB_NEXT);
+  }
+  switch(err) {
+  case DB_LOCK_DEADLOCK:
+    trackdb_closecursor(cursor);
+    return err;
+  case DB_NOTFOUND:
+    return trackdb_closecursor(cursor);
+  case 0:
+    assert(!"cannot happen");
+  default:
+    disorder_fatal(0, "error reading %s: %s", dbname, db_strerror(err));
+  }
+}
+
+static struct {
+  int letter;
+  const char *dbname;
+  DB **db;
+} dbtable[] = {
+  { 'P', "prefs.db",     &trackdb_prefsdb },
+  { 'G', "global.db",    &trackdb_globaldb },
+  { 'U', "users.db",     &trackdb_usersdb },
+  { 'W', "schedule.db",  &trackdb_scheduledb },
+  { 'L', "playlists.db", &trackdb_playlistsdb },
+  /* avoid 'T' and 'S' for now */
+};
+#define NDBTABLE (sizeof dbtable / sizeof *dbtable)
+
 /* dump prefs to FP, return nonzero on error */
-static void do_dump(FILE *fp, const char *tag,
-                   int tracksdb, int searchdb) {
-  DBC *cursor = 0;
+static void do_dump(FILE *fp, const char *tag) {
   DB_TXN *tid;
   struct sink *s = sink_stdio(tag, fp);
-  int err;
-  DBT k, d;
 
   for(;;) {
     tid = trackdb_begin_transaction();
     if(fseek(fp, 0, SEEK_SET) < 0)
-      fatal(errno, "error calling fseek");
+      disorder_fatal(errno, "error calling fseek");
     if(fflush(fp) < 0)
-      fatal(errno, "error calling fflush");
+      disorder_fatal(errno, "error calling fflush");
     if(ftruncate(fileno(fp), 0) < 0)
-      fatal(errno, "error calling ftruncate");
-    if(fprintf(fp, "V%c\n", (tracksdb || searchdb) ? '1' : '0') < 0)
-      fatal(errno, "error writing to %s", tag);
-    /* dump the preferences */
-    cursor = trackdb_opencursor(trackdb_prefsdb, tid);
-    err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                        DB_FIRST);
-    while(err == 0) {
-      if(fputc('P', fp) < 0
-         || urlencode(s, k.data, k.size)
-         || fputc('\n', fp) < 0
-         || urlencode(s, d.data, d.size)
-         || fputc('\n', fp) < 0)
-        fatal(errno, "error writing to %s", tag);
-      err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                          DB_NEXT);
-    }
-    if(trackdb_closecursor(cursor)) { cursor = 0; goto fail; }
-    cursor = 0;
-
-    /* dump the global preferences */
-    cursor = trackdb_opencursor(trackdb_globaldb, tid);
-    err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                        DB_FIRST);
-    while(err == 0) {
-      if(fputc('G', fp) < 0
-         || urlencode(s, k.data, k.size)
-         || fputc('\n', fp) < 0
-         || urlencode(s, d.data, d.size)
-         || fputc('\n', fp) < 0)
-        fatal(errno, "error writing to %s", tag);
-      err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                          DB_NEXT);
-    }
-    if(trackdb_closecursor(cursor)) { cursor = 0; goto fail; }
-    cursor = 0;
+      disorder_fatal(errno, "error calling ftruncate");
+    if(fprintf(fp, "V0") < 0)
+      disorder_fatal(errno, "error writing to %s", tag);
+    for(size_t n = 0; n < NDBTABLE; ++n)
+      if(dump_one(s, tag,
+                  dbtable[n].letter, dbtable[n].dbname, *dbtable[n].db,
+                  tid))
+        goto fail;
     
-    /* dump the users */
-    cursor = trackdb_opencursor(trackdb_usersdb, tid);
-    err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                        DB_FIRST);
-    while(err == 0) {
-      if(fputc('U', fp) < 0
-         || urlencode(s, k.data, k.size)
-         || fputc('\n', fp) < 0
-         || urlencode(s, d.data, d.size)
-         || fputc('\n', fp) < 0)
-        fatal(errno, "error writing to %s", tag);
-      err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                          DB_NEXT);
-    }
-    if(trackdb_closecursor(cursor)) { cursor = 0; goto fail; }
-    cursor = 0;
-
-    /* dump the schedule */
-    cursor = trackdb_opencursor(trackdb_scheduledb, tid);
-    err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                        DB_FIRST);
-    while(err == 0) {
-      if(fputc('W', fp) < 0
-         || urlencode(s, k.data, k.size)
-         || fputc('\n', fp) < 0
-         || urlencode(s, d.data, d.size)
-         || fputc('\n', fp) < 0)
-        fatal(errno, "error writing to %s", tag);
-      err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                          DB_NEXT);
-    }
-    if(trackdb_closecursor(cursor)) { cursor = 0; goto fail; }
-    cursor = 0;
-    
-    
-    if(tracksdb) {
-      cursor = trackdb_opencursor(trackdb_tracksdb, tid);
-      err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                         DB_FIRST);
-      while(err == 0) {
-       if(fputc('T', fp) < 0
-          || urlencode(s, k.data, k.size)
-          || fputc('\n', fp) < 0
-          || urlencode(s, d.data, d.size)
-          || fputc('\n', fp) < 0)
-         fatal(errno, "error writing to %s", tag);
-       err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                           DB_NEXT);
-      }
-      if(trackdb_closecursor(cursor)) { cursor = 0; goto fail; }
-      cursor = 0;
-    }
-
-    if(searchdb) {
-      cursor = trackdb_opencursor(trackdb_searchdb, tid);
-      err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                         DB_FIRST);
-      while(err == 0) {
-       if(fputc('S', fp) < 0
-          || urlencode(s, k.data, k.size)
-          || fputc('\n', fp) < 0
-          || urlencode(s, d.data, d.size)
-          || fputc('\n', fp) < 0)
-         fatal(errno, "error writing to %s", tag);
-       err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
-                           DB_NEXT);
-      }
-      if(trackdb_closecursor(cursor)) { cursor = 0; goto fail; }      cursor = 0;
-    }
-
-    if(fputs("E\n", fp) < 0) fatal(errno, "error writing to %s", tag);
-    if(err == DB_LOCK_DEADLOCK) {
-      error(0, "c->c_get: %s", db_strerror(err));
-      goto fail;
-    }
-    if(err && err != DB_NOTFOUND)
-      fatal(0, "cursor->c_get: %s", db_strerror(err));
-    if(trackdb_closecursor(cursor)) { cursor = 0; goto fail; }
+    if(fputs("E\n", fp) < 0)
+      disorder_fatal(errno, "error writing to %s", tag);
     break;
 fail:
-    trackdb_closecursor(cursor);
-    cursor = 0;
-    info("aborting transaction and retrying dump");
+    disorder_info("aborting transaction and retrying dump");
     trackdb_abort_transaction(tid);
   }
   trackdb_commit_transaction(tid);
-  if(fflush(fp) < 0) fatal(errno, "error writing to %s", tag);
+  if(fflush(fp) < 0) disorder_fatal(errno, "error writing to %s", tag);
   /* caller might not be paranoid so we are paranoid on their behalf */
-  if(fsync(fileno(fp)) < 0) fatal(errno, "error syncing %s", tag);
+  if(fsync(fileno(fp)) < 0) disorder_fatal(errno, "error syncing %s", tag);
 }
 
 /* delete all aliases prefs, return 0 or DB_LOCK_DEADLOCK */
@@ -207,11 +155,11 @@ static int remove_aliases(DB_TXN *tid, int remove_pathless) {
   struct kvp *data;
   int alias, pathless;
 
-  info("removing aliases");
+  disorder_info("removing aliases");
   cursor = trackdb_opencursor(trackdb_tracksdb, tid);
   if((err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
                           DB_FIRST)) == DB_LOCK_DEADLOCK) {
-    error(0, "cursor->c_get: %s", db_strerror(err));
+    disorder_error(0, "cursor->c_get: %s", db_strerror(err));
     goto done;
   }
   while(err == 0) {
@@ -219,24 +167,25 @@ static int remove_aliases(DB_TXN *tid, int remove_pathless) {
     alias = !!kvp_get(data, "_alias_for");
     pathless = !kvp_get(data, "_path");
     if(pathless && !remove_pathless)
-      info("no _path for %s", utf82mb(xstrndup(k.data, k.size)));
+      disorder_info("no _path for %s", utf82mb(xstrndup(k.data, k.size)));
     if(alias || (remove_pathless && pathless)) {
       switch(err = cursor->c_del(cursor, 0)) {
       case 0: break;
       case DB_LOCK_DEADLOCK:
-        error(0, "cursor->c_get: %s", db_strerror(err));
+        disorder_error(0, "cursor->c_get: %s", db_strerror(err));
         goto done;
       default:
-        fatal(0, "cursor->c_del: %s", db_strerror(err));
+        disorder_fatal(0, "cursor->c_del: %s", db_strerror(err));
       }
     }
     err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d), DB_NEXT);
   }
   if(err == DB_LOCK_DEADLOCK) {
-    error(0, "cursor operation: %s", db_strerror(err));
+    disorder_error(0, "cursor operation: %s", db_strerror(err));
     goto done;
   }
-  if(err != DB_NOTFOUND) fatal(0, "cursor->c_get: %s", db_strerror(err));
+  if(err != DB_NOTFOUND)
+    disorder_fatal(0, "cursor->c_get: %s", db_strerror(err));
   err = 0;
 done:
   if(trackdb_closecursor(cursor) && !err) err = DB_LOCK_DEADLOCK;
@@ -251,10 +200,10 @@ static int truncdb(DB_TXN *tid, DB *db) {
   switch(err = db->truncate(db, tid, &count, 0)) {
   case 0: break;
   case DB_LOCK_DEADLOCK:
-    error(0, "db->truncate: %s", db_strerror(err));
+    disorder_error(0, "db->truncate: %s", db_strerror(err));
     break;
   default:
-    fatal(0, "db->truncate: %s", db_strerror(err));
+    disorder_fatal(0, "db->truncate: %s", db_strerror(err));
   }
   return err;
 }
@@ -267,7 +216,7 @@ static int undump_dbt(FILE *fp, const char *tag, DBT *dbt) {
   if(inputline(tag, fp, &s, '\n')) return -1;
   dynstr_init(&d);
   if(urldecode(sink_dynstr(&d), s, strlen(s)))
-    fatal(0, "invalid URL-encoded data in %s", tag);
+    disorder_fatal(0, "invalid URL-encoded data in %s", tag);
   dbt->data = d.vec;
   dbt->size = d.nvec;
   return 0;
@@ -276,13 +225,10 @@ static int undump_dbt(FILE *fp, const char *tag, DBT *dbt) {
 /* undump from FP, return 0 or DB_LOCK_DEADLOCK */
 static int undump_from_fp(DB_TXN *tid, FILE *fp, const char *tag) {
   int err, c;
-  DBT k, d;
-  const char *which_name;
-  DB *which_db;
 
-  info("undumping");
+  disorder_info("undumping");
   if(fseek(fp, 0, SEEK_SET) < 0)
-    fatal(errno, "error calling fseek on %s", tag);
+    disorder_fatal(errno, "error calling fseek on %s", tag);
   if((err = truncdb(tid, trackdb_prefsdb))) return err;
   if((err = truncdb(tid, trackdb_globaldb))) return err;
   if((err = truncdb(tid, trackdb_searchdb))) return err;
@@ -291,68 +237,51 @@ static int undump_from_fp(DB_TXN *tid, FILE *fp, const char *tag) {
   if((err = truncdb(tid, trackdb_scheduledb))) return err;
   c = getc(fp);
   while(!ferror(fp) && !feof(fp)) {
+    for(size_t n = 0; n < NDBTABLE; ++n) {
+      if(dbtable[n].letter == c) {
+       DB *db = *dbtable[n].db;
+       const char *dbname = dbtable[n].dbname;
+        DBT k, d;
+
+        if(undump_dbt(fp, tag, prepare_data(&k))
+           || undump_dbt(fp, tag, prepare_data(&d)))
+          break;
+        switch(err = db->put(db, tid, &k, &d, 0)) {
+        case 0:
+          break;
+        case DB_LOCK_DEADLOCK:
+          disorder_error(0, "error updating %s: %s", dbname, db_strerror(err));
+          return err;
+        default:
+          disorder_fatal(0, "error updating %s: %s", dbname, db_strerror(err));
+        }
+        goto next;
+      }
+    }
+    
     switch(c) {
     case 'V':
       c = getc(fp);
       if(c != '0')
-        fatal(0, "unknown version '%c'", c);
+        disorder_fatal(0, "unknown version '%c'", c);
       break;
     case 'E':
       return 0;
-    case 'P':
-    case 'G':
-    case 'U':
-    case 'W':
-      switch(c) {
-      case 'P':
-       which_db = trackdb_prefsdb;
-       which_name = "prefs.db";
-       break;
-      case 'G':
-       which_db = trackdb_globaldb;
-       which_name = "global.db";
-       break;
-      case 'U':
-       which_db = trackdb_usersdb;
-       which_name = "users.db";
-       break;
-      case 'W':                                /* for 'when' */
-       which_db = trackdb_scheduledb;
-       which_name = "scheduledb.db";
-       break;
-      default:
-       abort();
-      }
-      if(undump_dbt(fp, tag, prepare_data(&k))
-         || undump_dbt(fp, tag, prepare_data(&d)))
-        break;
-      switch(err = which_db->put(which_db, tid, &k, &d, 0)) {
-      case 0:
-        break;
-      case DB_LOCK_DEADLOCK:
-        error(0, "error updating %s: %s", which_name, db_strerror(err));
-        return err;
-      default:
-        fatal(0, "error updating %s: %s", which_name, db_strerror(err));
-      }
-      break;
-    case 'T':
-    case 'S':
-      if(undump_dbt(fp, tag, prepare_data(&k))
-         || undump_dbt(fp, tag, prepare_data(&d)))
-        break;
-      /* We don't restore the tracks.db or search.db entries, instead
-       * we recompute them */
-      break;
     case '\n':
       break;
+    default:
+      if(c >= 32 && c <= 126)
+        disorder_fatal(0, "unexpected character '%c'", c);
+      else
+        disorder_fatal(0, "unexpected character 0x%02X", c);
     }
+  next:
     c = getc(fp);
   }
   if(ferror(fp))
-    fatal(errno, "error reading %s", tag);
+    disorder_fatal(errno, "error reading %s", tag);
   else
-    fatal(0, "unexpected EOF reading %s", tag);
+    disorder_fatal(0, "unexpected EOF reading %s", tag);
   return 0;
 }
 
@@ -365,7 +294,7 @@ static int recompute_aliases(DB_TXN *tid) {
   struct kvp *data;
   const char *path, *track;
 
-  info("recomputing aliases");
+  disorder_info("recomputing aliases");
   cursor = trackdb_opencursor(trackdb_tracksdb, tid);
   if((err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
                           DB_FIRST)) == DB_LOCK_DEADLOCK) goto done;
@@ -374,7 +303,7 @@ static int recompute_aliases(DB_TXN *tid) {
     track = xstrndup(k.data, k.size);
     if(!kvp_get(data, "_alias_for")) {
       if(!(path = kvp_get(data, "_path")))
-       error(0, "%s is not an alias but has no path", utf82mb(track));
+       disorder_error(0, "%s is not an alias but has no path", utf82mb(track));
       else
        if((err = trackdb_notice_tid(track, path, tid)) == DB_LOCK_DEADLOCK)
          goto done;
@@ -391,7 +320,7 @@ static int recompute_aliases(DB_TXN *tid) {
   case DB_LOCK_DEADLOCK:
     break;
   default:
-    fatal(0, "cursor->c_get: %s", db_strerror(err));
+    disorder_fatal(0, "cursor->c_get: %s", db_strerror(err));
   }
 done:
   if(trackdb_closecursor(cursor) && !err) err = DB_LOCK_DEADLOCK;
@@ -409,10 +338,10 @@ static void do_undump(FILE *fp, const char *tag, int remove_pathless) {
        || recompute_aliases(tid)) goto fail;
     break;
 fail:
-    info("aborting transaction and retrying undump");
+    disorder_info("aborting transaction and retrying undump");
     trackdb_abort_transaction(tid);
   }
-  info("committing undump");
+  disorder_info("committing undump");
   trackdb_commit_transaction(tid);
 }
 
@@ -426,22 +355,22 @@ static void do_recompute(int remove_pathless) {
        || recompute_aliases(tid)) goto fail;
     break;
 fail:
-    info("aborting transaction and retrying recomputation");
+    disorder_info("aborting transaction and retrying recomputation");
     trackdb_abort_transaction(tid);
   }
-  info("committing recomputed aliases");
+  disorder_info("committing recomputed aliases");
   trackdb_commit_transaction(tid);
 }
 
 int main(int argc, char **argv) {
   int n, dump = 0, undump = 0, recover = TRACKDB_NO_RECOVER, recompute = 0;
-  int tracksdb = 0, searchdb = 0, remove_pathless = 0, fd;
+  int remove_pathless = 0, fd;
   const char *path;
   char *tmp;
   FILE *fp;
 
   mem_init();
-  while((n = getopt_long(argc, argv, "hVc:dDutsrRaP", options, 0)) >= 0) {
+  while((n = getopt_long(argc, argv, "hVc:dDurRaP", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
     case 'V': version("disorder-dump");
@@ -449,31 +378,28 @@ int main(int argc, char **argv) {
     case 'd': dump = 1; break;
     case 'u': undump = 1; break;
     case 'D': debugging = 1; break;
-    case 't': tracksdb = 1; break;
-    case 's': searchdb = 1; break;
     case 'r': recover = TRACKDB_NORMAL_RECOVER;
     case 'R': recover = TRACKDB_FATAL_RECOVER;
     case 'a': recompute = 1; break;
     case 'P': remove_pathless = 1; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   if(dump + undump + recompute != 1)
-    fatal(0, "choose exactly one of --dump, --undump or --recompute-aliases");
-  if((undump || recompute) && (tracksdb || searchdb))
-    fatal(0, "--trackdb and --searchdb with --undump or --recompute-aliases");
+    disorder_fatal(0, "choose exactly one of --dump, --undump or --recompute-aliases");
   if(recompute) {
     if(optind != argc)
-      fatal(0, "--recompute-aliases does not take a filename");
+      disorder_fatal(0, "--recompute-aliases does not take a filename");
     path = 0;
   } else {
     if(optind >= argc)
-      fatal(0, "missing dump file name");
+      disorder_fatal(0, "missing dump file name");
     if(optind + 1 < argc)
-      fatal(0, "specify only a dump file name");
+      disorder_fatal(0, "specify only a dump file name");
     path = argv[optind];
   }
-  if(config_read(0)) fatal(0, "cannot read configuration");
+  if(config_read(0, NULL))
+    disorder_fatal(0, "cannot read configuration");
   trackdb_init(recover|TRACKDB_MAY_CREATE);
   trackdb_open(TRACKDB_NO_UPGRADE);
   if(dump) {
@@ -481,25 +407,27 @@ int main(int argc, char **argv) {
      * sure the permissions are tight from the start. */
     byte_xasprintf(&tmp, "%s.%lx.tmp", path, (unsigned long)getpid());
     if((fd = open(tmp, O_CREAT|O_TRUNC|O_WRONLY, 0600)) < 0)
-      fatal(errno, "error opening %s", tmp);
+      disorder_fatal(errno, "error opening %s", tmp);
     if(!(fp = fdopen(fd, "w")))
-      fatal(errno, "fdopen on %s", tmp);
-    do_dump(fp, tmp, tracksdb, searchdb);
-    if(fclose(fp) < 0) fatal(errno, "error closing %s", tmp);
+      disorder_fatal(errno, "fdopen on %s", tmp);
+    do_dump(fp, tmp);
+    if(fclose(fp) < 0) disorder_fatal(errno, "error closing %s", tmp);
     if(rename(tmp, path) < 0)
-      fatal(errno, "error renaming %s to %s", tmp, path);
+      disorder_fatal(errno, "error renaming %s to %s", tmp, path);
   } else if(undump) {
     /* the databases or logfiles might end up with wrong permissions
      * if new ones are created */
-    if(getuid() == 0) info("you might need to chown database files");
-    if(!(fp = fopen(path, "r"))) fatal(errno, "error opening %s", path);
+    if(getuid() == 0)
+      disorder_info("you might need to chown database files");
+    if(!(fp = fopen(path, "r")))
+      disorder_fatal(errno, "error opening %s", path);
     do_undump(fp, path, remove_pathless);
     xfclose(fp);
   } else if(recompute) {
     do_recompute(remove_pathless);
   }
   trackdb_close();
-  trackdb_deinit();
+  trackdb_deinit(NULL);
   return 0;
 }
 
diff --git a/server/endian.c b/server/endian.c
new file mode 100644 (file)
index 0000000..b7e9168
--- /dev/null
@@ -0,0 +1,23 @@
+/** @file server/endian.c
+ * @brief Expose runtime endianness to makefile for testing
+ */
+#include <config.h>
+#include <stdio.h>
+
+int main(void) {
+#if WORDS_BIGENDIAN
+  puts("1");
+#else
+  puts("0");
+#endif
+  return 0;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index db5b9d5..0f97a2f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder
- * Copyright (C) 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2007-2009 Richard Kettlewell
  *
  * 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
 /** @file server/normalize.c
  * @brief Convert "raw" format output to the configured format
  *
- * Currently we invoke sox even for trivial conversions such as byte-swapping.
- * Ideally we would do all conversion including resampling in this one process
- * and eliminate the dependency on sox.
+ * If libsamplerate is available then resample_convert() is used to do all
+ * conversions.  If not then we invoke sox (even for trivial conversions such
+ * as byte-swapping).  The sox support might be removed in a future version.
  */
 
 #include "disorder-server.h"
+#include "resample.h"
+
+static char buffer[1024 * 1024];
 
 static const struct option options[] = {
   { "help", no_argument, 0, 'h' },
@@ -59,7 +62,6 @@ static void help(void) {
  * @param n Number of bytes to copy
  */
 static void copy(int infd, int outfd, size_t n) {
-  char buffer[4096];
   ssize_t written;
 
   while(n > 0) {
@@ -69,21 +71,22 @@ static void copy(int infd, int outfd, size_t n) {
       if(errno == EINTR)
        continue;
       else
-       fatal(errno, "read error");
+       disorder_fatal(errno, "read error");
     }
     if(readden == 0)
-      fatal(0, "unexpected EOF");
+      disorder_fatal(0, "unexpected EOF");
     n -= readden;
     written = 0;
     while(written < readden) {
       const ssize_t w = write(outfd, buffer + written, readden - written);
       if(w < 0)
-       fatal(errno, "write error");
+       disorder_fatal(errno, "write error");
       written += w;
     }
   }
 }
 
+#if !HAVE_SAMPLERATE_H
 static void soxargs(const char ***pp, char **qq,
                     const struct stream_header *header) {
   *(*pp)++ = "-t.raw";
@@ -102,7 +105,7 @@ static void soxargs(const char ***pp, char **qq,
     case 16: *(*pp)++ = "-w"; break;
     case 32: *(*pp)++ = "-l"; break;
     case 64: *(*pp)++ = "-d"; break;
-    default: fatal(0, "cannot handle sample size %d", header->bits);
+    default: disorder_fatal(0, "cannot handle sample size %d", header->bits);
     }
     break;
   case 1:
@@ -113,22 +116,41 @@ static void soxargs(const char ***pp, char **qq,
       case ENDIAN_LITTLE: *(*pp)++ = "-L"; break;
       }
     if(header->bits % 8)
-      fatal(0, "cannot handle sample size %d", header->bits);
+      disorder_fatal(0, "cannot handle sample size %d", header->bits);
     *qq += sprintf((char *)(*(*pp)++ = *qq), "-%d", header->bits / 8) + 1;
     break;
   default:
-    fatal(0, "unknown sox_generation %ld", config->sox_generation);
+    disorder_fatal(0, "unknown sox_generation %ld", config->sox_generation);
+  }
+}
+#else
+static void converted(uint8_t *bytes,
+                      size_t nbytes,
+                      void attribute((unused)) *cd) {
+  /*syslog(LOG_INFO, "out: %02x %02x %02x %02x",
+         bytes[0],
+         bytes[1],
+         bytes[2],
+         bytes[3]);*/
+  while(nbytes > 0) {
+    ssize_t n = write(1, bytes, nbytes);
+    if(n < 0)
+      disorder_fatal(errno, "writing to stdout");
+    bytes += n;
+    nbytes -= n;
   }
 }
+#endif
 
 int main(int argc, char attribute((unused)) **argv) {
   struct stream_header header, latest_format;
-  int n, p[2], outfd = -1, logsyslog = !isatty(2);
+  int n, outfd = -1, logsyslog = !isatty(2), rs_in_use = 0;
   pid_t pid = -1;
+  struct resampler rs[1];
 
   set_progname(argv);
   if(!setlocale(LC_CTYPE, ""))
-    fatal(errno, "error calling setlocale");
+    disorder_fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "hVc:dDSs", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
@@ -138,55 +160,138 @@ int main(int argc, char attribute((unused)) **argv) {
     case 'D': debugging = 0; break;
     case 'S': logsyslog = 0; break;
     case 's': logsyslog = 1; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
-  if(config_read(1))
-    fatal(0, "cannot read configuration");
+  if(config_read(1, NULL))
+    disorder_fatal(0, "cannot read configuration");
   if(logsyslog) {
     openlog(progname, LOG_PID, LOG_DAEMON);
     log_default = &log_syslog;
   }
   memset(&latest_format, 0, sizeof latest_format);
   for(;;) {
+    /* Read one header */
     n = 0;
     while((size_t)n < sizeof header) {
       int r = read(0, (char *)&header + n, sizeof header - n);
 
       if(r < 0) {
         if(errno != EINTR)
-          fatal(errno, "error reading header");
+          disorder_fatal(errno, "error reading header");
       } else if(r == 0) {
         if(n)
-          fatal(0, "EOF reading header");
+          disorder_fatal(0, "EOF reading header");
         break;
       } else
         n += r;
     }
     if(!n)
       break;
+    D(("NEW HEADER: %"PRIu32" bytes %"PRIu32"Hz %"PRIu8" channels %"PRIu8" bits %"PRIu8" endian",
+       header.nbytes, header.rate, header.channels, header.bits, header.endian));
     /* Sanity check the header */
     if(header.rate < 100 || header.rate > 1000000)
-      fatal(0, "implausible rate %"PRId32"Hz (%#"PRIx32")",
-            header.rate, header.rate);
+      disorder_fatal(0, "implausible rate %"PRId32"Hz (%#"PRIx32")",
+                     header.rate, header.rate);
     if(header.channels < 1 || header.channels > 2)
-      fatal(0, "unsupported channel count %d", header.channels);
+      disorder_fatal(0, "unsupported channel count %d", header.channels);
     if(header.bits % 8 || !header.bits || header.bits > 64)
-      fatal(0, "unsupported sample size %d bits", header.bits);
+      disorder_fatal(0, "unsupported sample size %d bits", header.bits);
     if(header.endian != ENDIAN_BIG && header.endian != ENDIAN_LITTLE)
-      fatal(0, "unsupported byte order %x", header.bits);
+      disorder_fatal(0, "unsupported byte order %d", header.endian);
     /* Skip empty chunks regardless of their alleged format */
     if(header.nbytes == 0)
       continue;
     /* If the format has changed we stop/start the converter */
+#if HAVE_SAMPLERATE_H
+    /* We have libsamplerate */
+    if(formats_equal(&header, &config->sample_format))
+      /* If the format is already correct then we just write out the data */
+      copy(0, 1, header.nbytes);
+    else {
+      /* If we have a resampler active already check it is suitable and destroy
+       * it if not */
+      if(rs_in_use) {
+        D(("call resample_close"));
+        resample_close(rs);
+        rs_in_use = 0;
+      }
+      /*syslog(LOG_INFO, "%d/%d/%d/%d/%d -> %d/%d/%d/%d/%d",
+             header.bits,
+             header.channels, 
+             header.rate,
+             1,
+             header.endian,
+             config->sample_format.bits,
+             config->sample_format.channels, 
+             config->sample_format.rate,
+             1,
+             config->sample_format.endian);*/
+      if(!rs_in_use) {
+        /* Create a suitable resampler. */
+        D(("call resample_init"));
+        resample_init(rs,
+                      header.bits,
+                      header.channels, 
+                      header.rate,
+                      1,                /* signed */
+                      header.endian,
+                      config->sample_format.bits,
+                      config->sample_format.channels, 
+                      config->sample_format.rate,
+                      1,                /* signed */
+                      config->sample_format.endian);
+        latest_format = header;
+        rs_in_use = 1;
+        /* TODO speaker protocol does not record signedness of samples.  It's
+         * assumed that they are always signed.  This should be fixed in the
+         * future (and the sample format syntax extended in a compatible
+         * way). */
+      }
+      /* Feed data through the resampler */
+      size_t used = 0, left = header.nbytes;
+      while(used || left) {
+        if(left) {
+          size_t limit = (sizeof buffer) - used;
+          if(limit > left)
+            limit = left;
+          ssize_t r = read(0, buffer + used, limit);
+          if(r < 0)
+            disorder_fatal(errno, "reading from stdin");
+          if(r == 0)
+            disorder_fatal(0, "unexpected EOF");
+          left -= r;
+          used += r;
+          //syslog(LOG_INFO, "read %zd bytes", r);
+          D(("read %zd bytes", r));
+        }
+        /*syslog(LOG_INFO, " in: %02x %02x %02x %02x",
+               (uint8_t)buffer[0],
+               (uint8_t)buffer[1], 
+               (uint8_t)buffer[2],
+               (uint8_t)buffer[3]);*/
+        D(("calling resample_convert used=%zu !left=%d", used, !left));
+        const size_t consumed = resample_convert(rs,
+                                                 (uint8_t *)buffer, used,
+                                                 !left,
+                                                 converted, 0);
+        //syslog(LOG_INFO, "used=%zu consumed=%zu", used, consumed);
+        D(("consumed=%zu", consumed));
+        memmove(buffer, buffer + consumed, used - consumed);
+        used -= consumed;
+      }
+    }
+#else
+    /* We do not have libsamplerate.  We will use sox instead. */
     if(!formats_equal(&header, &latest_format)) {
       if(pid != -1) {
         /* There's a running converter, stop it */
         xclose(outfd);
         if(waitpid(pid, &n, 0) < 0)
-          fatal(errno, "error calling waitpid");
+          disorder_fatal(errno, "error calling waitpid");
         if(n)
-          fatal(0, "sox failed: %#x", n);
+          disorder_fatal(0, "sox failed: %#x", n);
         pid = -1;
         outfd = -1;
       }
@@ -202,6 +307,7 @@ int main(int argc, char attribute((unused)) **argv) {
         *pp++ = "-";                  /* stdout */
         *pp = 0;
         /* This pipe will be sox's stdin */
+        int p[2];
         xpipe(p);
         if(!(pid = xfork())) {
           exitfn = _exit;
@@ -209,7 +315,7 @@ int main(int argc, char attribute((unused)) **argv) {
           xclose(p[0]);
           xclose(p[1]);
           execvp(av[0], (char **)av);
-          fatal(errno, "sox");
+          disorder_fatal(errno, "sox");
         }
         xclose(p[0]);
         outfd = p[1];
@@ -221,16 +327,19 @@ int main(int argc, char attribute((unused)) **argv) {
     }
     /* Convert or copy this chunk */
     copy(0, outfd, header.nbytes);
+#endif
   }
   if(outfd != -1)
     xclose(outfd);
   if(pid != -1) {
     /* There's still a converter running */
     if(waitpid(pid, &n, 0) < 0)
-      fatal(errno, "error calling waitpid");
+      disorder_fatal(errno, "error calling waitpid");
     if(n)
-      fatal(0, "sox failed: %#x", n);
+      disorder_fatal(0, "sox failed: %#x", n);
   }
+  if(rs_in_use)
+    resample_close(rs);
   return 0;
 }
 
index 93a7f48..99ef483 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004-2008 Richard Kettlewell
+ * Copyright (C) 2004-2009 Richard Kettlewell
  *
  * 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
@@ -17,6 +17,8 @@
  */
 /** @file server/play.c
  * @brief Playing tracks
+ *
+ * This file is rather badly organized.  Sorry.  It's better than it was...
  */
 
 #include "disorder-server.h"
 
 #define SPEAKER "disorder-speaker"
 
+/** @brief The current playing track or NULL */
 struct queue_entry *playing;
+
+/** @brief Set when paused */
 int paused;
 
 static void finished(ev_source *ev);
-
+static int start_child(struct queue_entry *q, 
+                       const struct pbgc_params *params,
+                       void attribute((unused)) *bgdata);
+static int prepare_child(struct queue_entry *q, 
+                         const struct pbgc_params *params,
+                         void attribute((unused)) *bgdata);
+static void ensure_next_scratch(ev_source *ev);
+
+/** @brief File descriptor of our end of the socket to the speaker */
 static int speaker_fd = -1;
-static hash *player_pids;
-static int shutting_down;
-
-static void store_player_pid(const char *id, pid_t pid) {
-  if(!player_pids) player_pids = hash_new(sizeof (pid_t));
-  hash_add(player_pids, id, &pid, HASH_INSERT_OR_REPLACE);
-}
-
-static pid_t find_player_pid(const char *id) {
-  pid_t *pidp;
 
-  if(player_pids && (pidp = hash_find(player_pids, id))) return *pidp;
-  return -1;
-}
+/** @brief Set when shutting down */
+static int shutting_down;
 
-static void forget_player_pid(const char *id) {
-  if(player_pids) hash_remove(player_pids, id);
-}
+/* Speaker ------------------------------------------------------------------ */
 
-/* called when speaker process terminates */
+/** @brief Called when speaker process terminates
+ *
+ * Currently kills of DisOrder completely.  A future version could terminate
+ * the speaker when nothing was going on, or recover from failures, though any
+ * tracks with decoders already started would need to have them restarted.
+ */
 static int speaker_terminated(ev_source attribute((unused)) *ev,
                              pid_t attribute((unused)) pid,
                              int attribute((unused)) status,
                              const struct rusage attribute((unused)) *rusage,
                              void attribute((unused)) *u) {
-  fatal(0, "speaker subprocess %s",
-       wstat(status));
+  disorder_fatal(0, "speaker subprocess %s", wstat(status));
 }
 
-/* called when speaker process has something to say */
+/** @brief Called when we get a message from the speaker process */
 static int speaker_readable(ev_source *ev, int fd,
                            void attribute((unused)) *u) {
   struct speaker_message sm;
@@ -79,27 +83,46 @@ static int speaker_readable(ev_source *ev, int fd,
   case SM_FINISHED:                    /* scratched the playing track */
   case SM_STILLBORN:                   /* scratched too early */
   case SM_UNKNOWN:                     /* scratched WAY too early */
-    if(playing && !strcmp(sm.id, playing->id))
+    if(playing && !strcmp(sm.id, playing->id)) {
+      if((playing->state == playing_unplayed
+          || playing->state == playing_started)
+         && sm.type == SM_FINISHED)
+        playing->state = playing_ok;
       finished(ev);
+    }
     break;
   case SM_PLAYING:
     /* track ID is playing, DATA seconds played */
     D(("SM_PLAYING %s %ld", sm.id, sm.data));
     playing->sofar = sm.data;
     break;
+  case SM_ARRIVED: {
+    /* track ID is now prepared */
+    struct queue_entry *q;
+    for(q = qhead.next; q != &qhead && strcmp(q->id, sm.id); q = q->next)
+      ;
+    if(q && q->preparing) {
+      q->preparing = 0;
+      q->prepared = 1;
+      /* We might be waiting to play the now-prepared track */
+      play(ev);
+    }
+    break;
+  }
   default:
-    error(0, "unknown message type %d", sm.type);
+    disorder_error(0, "unknown speaker message type %d", sm.type);
   }
   return 0;
 }
 
+/** @brief Initialize the speaker process */
 void speaker_setup(ev_source *ev) {
   int sp[2];
   pid_t pid;
   struct speaker_message sm;
 
   if(socketpair(PF_UNIX, SOCK_DGRAM, 0, sp) < 0)
-    fatal(errno, "error calling socketpair");
+    disorder_fatal(errno, "error calling socketpair");
   if(!(pid = xfork())) {
     exitfn = _exit;
     ev_signal_atfork(ev);
@@ -119,7 +142,7 @@ void speaker_setup(ev_source *ev) {
           log_default == &log_syslog ? "--syslog" : "--no-syslog",
           (char *)0);
 #endif
-    fatal(errno, "error invoking %s", SPEAKER);
+    disorder_fatal(errno, "error invoking %s", SPEAKER);
   }
   ev_child(ev, pid, 0, speaker_terminated, 0);
   speaker_fd = sp[1];
@@ -129,9 +152,10 @@ void speaker_setup(ev_source *ev) {
   speaker_recv(speaker_fd, &sm);
   nonblock(speaker_fd);
   if(ev_fd(ev, ev_read, speaker_fd, speaker_readable, 0, "speaker read") < 0)
-    fatal(0, "error registering speaker socket fd");
+    disorder_fatal(0, "error registering speaker socket fd");
 }
 
+/** @brief Tell the speaker to reload its configuration */
 void speaker_reload(void) {
   struct speaker_message sm;
 
@@ -140,9 +164,23 @@ void speaker_reload(void) {
   speaker_send(speaker_fd, &sm);
 }
 
-/* Called when the currently playing track finishes playing.  This
- * might be because the player finished or because the speaker process
- * told us so. */
+/* Track termination -------------------------------------------------------- */
+
+/** @brief Called when the currently playing track finishes playing
+ * @param ev Event loop or NULL
+ *
+ * There are three places this is called from:
+ * 
+ * 1) speaker_readable(), when the speaker tells us the playing track finished.
+ * (Technically the speaker lies a little to arrange for gapless play.)
+ *
+ * 2) player_finished(), when the player for a non-raw track (i.e. one that
+ * does not use the speaker) finishes.
+ *
+ * 3) quitting(), after signalling the decoder or player but possible before it
+ * has actually terminated.  In this case @p ev is NULL, inhibiting any further
+ * attempt to play anything.
+ */
 static void finished(ev_source *ev) {
   D(("finished playing=%p", (void *)playing));
   if(!playing)
@@ -164,15 +202,22 @@ static void finished(ev_source *ev) {
   }
   queue_played(playing);
   recent_write();
-  forget_player_pid(playing->id);
   playing = 0;
   /* Try to play something else */
-  /* TODO re-support config->gap? */
   if(ev)
     play(ev);
 }
 
-/* Called when a player terminates. */
+/** @brief Called when a player or decoder process terminates
+ *
+ * This is called when a decoder process terminates (which might actually be
+ * some time before the speaker reports it as finished) or when a non-raw
+ * (i.e. non-speaker) player terminates.  In the latter case it's imaginable
+ * that the OS has buffered the last few samples.
+ *
+ * NB.  The finished track might NOT be in the queue (yet) - it might be a
+ * pre-chosen scratch.
+ */
 static int player_finished(ev_source *ev,
                           pid_t pid,
                           int status,
@@ -185,18 +230,20 @@ static int player_finished(ev_source *ev,
   /* Record that this PID is dead.  If we killed the track we might know this
    * already, but also it might have exited or crashed.  Either way we don't
    * want to end up signalling it. */
-  if(pid == find_player_pid(q->id))
-    forget_player_pid(q->id);
+  q->pid = -1;
   switch(q->state) {
   case playing_unplayed:
   case playing_random:
     /* If this was a pre-prepared track then either it failed or we
-     * deliberately stopped it because it was removed from the queue or moved
-     * down it.  So leave it state alone for future use. */
+     * deliberately stopped it: it might have been removed from the queue, or
+     * moved down the queue, or the speaker might be on a break.  So we leave
+     * it state alone for future use.
+     */
     break;
   default:
     /* We actually started playing this track. */
     if(status) {
+      /* Don't override 'scratched' with 'failed'. */
       if(q->state != playing_scratched)
        q->state = playing_failed;
     } else 
@@ -206,7 +253,7 @@ static int player_finished(ev_source *ev,
   /* Regardless we always report and record the status and do cleanup for
    * prefork calls. */
   if(status)
-    error(0, "player for %s %s", q->track, wstat(status));
+    disorder_error(0, "player for %s %s", q->track, wstat(status));
   if(q->type & DISORDER_PLAYER_PREFORK)
     play_cleanup(q->pl, q->data);
   q->wstat = status;
@@ -219,262 +266,257 @@ static int player_finished(ev_source *ev,
   return 0;
 }
 
-/* Find the player for Q */
-static int find_player(const struct queue_entry *q) {
+/* Track initiation --------------------------------------------------------- */
+
+/** @brief Find the player for @p q */
+static const struct stringlist *find_player(const struct queue_entry *q) {
   int n;
   
   for(n = 0; n < config->player.n; ++n)
     if(fnmatch(config->player.s[n].s[0], q->track, 0) == 0)
       break;
   if(n >= config->player.n)
-    return -1;
+    return NULL;
   else
-    return n;
+    return &config->player.s[n];
 }
 
-/* Return values from start() */
-#define START_OK 0                     /**< @brief Succeeded. */
-#define START_HARDFAIL 1               /**< @brief Track is broken. */
-#define START_SOFTFAIL 2          /**< @brief Track OK, system (temporarily?) broken */
-
-/** @brief Play or prepare @p q
+/** @brief Start to play @p q
  * @param ev Event loop
  * @param q Track to play/prepare
- * @param prepare_only If true, only prepares track
  * @return @ref START_OK, @ref START_HARDFAIL or @ref START_SOFTFAIL
+ *
+ * This makes @p actually start playing.  It calls prepare() if necessary and
+ * either sends an @ref SM_PLAY command or invokes the player itself in a
+ * subprocess.
+ *
+ * It's up to the caller to set @ref playing and @c playing->state (this might
+ * be changed in the future).
  */
 static int start(ev_source *ev,
-                struct queue_entry *q,
-                int prepare_only) {
-  int n, lfd;
-  const char *p;
-  int np[2], sfd;
-  struct speaker_message sm;
-  char buffer[64];
-  int optc;
-  ao_sample_format format;
-  ao_device *device;
-  int retries;
-  struct timespec ts;
-  const char *waitdevice = 0;
-  const char *const *optv;
-  pid_t pid, npid;
-  struct sockaddr_un addr;
-  uint32_t l;
+                struct queue_entry *q) {
+  const struct stringlist *player;
+  int rc;
 
-  memset(&sm, 0, sizeof sm);
-  D(("start %s %d", q->id, prepare_only));
-  if(q->prepared) {
-    /* The track is alraedy prepared */
-    if(!prepare_only) {
-      /* We want to run it, since it's prepared the answer is to tell the
-       * speaker to set it off */
-      strcpy(sm.id, q->id);
-      sm.type = SM_PLAY;
-      speaker_send(speaker_fd, &sm);
-      D(("sent SM_PLAY for %s", sm.id));
-    }
-    return START_OK;
-  }
+  D(("start %s", q->id));
   /* Find the player plugin. */
-  if((n = find_player(q)) < 0) return START_HARDFAIL;
-  if(!(q->pl = open_plugin(config->player.s[n].s[1], 0)))
+  if(!(player = find_player(q)) < 0)
+    return START_HARDFAIL;              /* No player */
+  if(!(q->pl = open_plugin(player->s[1], 0)))
     return START_HARDFAIL;
   q->type = play_get_type(q->pl);
-  /* Can't prepare non-raw tracks. */
-  if(prepare_only
-     && (q->type & DISORDER_PLAYER_TYPEMASK) != DISORDER_PLAYER_RAW)
+  /* Special handling for raw-format players */
+  if((q->type & DISORDER_PLAYER_TYPEMASK) == DISORDER_PLAYER_RAW) {
+    /* Make sure that the track is prepared */
+    if((rc = prepare(ev, q)))
+      return rc;
+    /* Now we're sure it's prepared, start it playing */
+    /* TODO actually it might not be fully prepared yet - it's all happening in
+     * a subprocess.  See speaker.c for further discussion.  */
+    struct speaker_message sm[1];
+    memset(sm, 0, sizeof sm);
+    strcpy(sm->id, q->id);
+    sm->type = SM_PLAY;
+    speaker_send(speaker_fd, sm);
+    D(("sent SM_PLAY for %s", sm->id));
+    /* Our caller will set playing and playing->state = playing_started */
     return START_OK;
-  /* Call the prefork function. */
-  p = trackdb_rawpath(q->track);
-  if(q->type & DISORDER_PLAYER_PREFORK)
-    if(!(q->data = play_prefork(q->pl, p))) {
-      error(0, "prefork function for %s failed", q->track);
-      return START_HARDFAIL;
-    }
-  /* Use the second arg as the tag if available (it's probably a command name),
-   * otherwise the module name. */
-  if(!isatty(2))
-    lfd = logfd(ev, (config->player.s[n].s[2]
-                    ? config->player.s[n].s[2] : config->player.s[n].s[1]));
-  else
-    lfd = -1;
-  optc = config->player.s[n].n - 2;
-  optv = (void *)&config->player.s[n].s[2];
-  while(optc > 0 && optv[0][0] == '-') {
-    if(!strcmp(optv[0], "--")) {
-      ++optv;
-      --optc;
-      break;
-    }
-    if(!strcmp(optv[0], "--wait-for-device")
-       || !strncmp(optv[0], "--wait-for-device=", 18)) {
-      if((waitdevice = strchr(optv[0], '='))) {
-       ++waitdevice;
-      } else
-       waitdevice = "";                /* use default */
-      ++optv;
-      --optc;
-    } else {
-      error(0, "unknown option %s", optv[0]);
-      return START_HARDFAIL;
-    }
+  } else {
+    rc = play_background(ev, player, q, start_child, NULL);
+    if(rc == START_OK)
+      ev_child(ev, q->pid, 0, player_finished, q);
+      /* Our caller will set playing and playing->state = playing_started */
+    return rc;
   }
-  switch(pid = fork()) {
-  case 0:                      /* child */
-    exitfn = _exit;
-    progname = "disorderd-fork";
-    ev_signal_atfork(ev);
-    signal(SIGPIPE, SIG_DFL);
-    if(lfd != -1) {
-      xdup2(lfd, 1);
-      xdup2(lfd, 2);
-      xclose(lfd);                     /* tidy up */
-    }
-    setpgid(0, 0);
-    if((q->type & DISORDER_PLAYER_TYPEMASK) == DISORDER_PLAYER_RAW) {
-      /* "Raw" format players always have their output send down a pipe
-       * to the disorder-normalize process.  This will connect to the
-       * speaker process to actually play the audio data.
-       */
-      /* np will be the pipe to disorder-normalize */
-      if(socketpair(PF_UNIX, SOCK_STREAM, 0, np) < 0)
-       fatal(errno, "error calling socketpair");
-      /* Beware of the Leopard!  On OS X 10.5.x, the order of the shutdown
-       * calls here DOES MATTER.  If you do the SHUT_WR first then the SHUT_RD
-       * fails with "Socket is not connected".  I think this is a bug but
-       * provided implementors either don't care about the order or all agree
-       * about the order, choosing the reliable order is an adequate
-       * workaround.  */
-      xshutdown(np[1], SHUT_RD);       /* decoder writes to np[1] */
-      xshutdown(np[0], SHUT_WR);       /* normalize reads from np[0] */
-      blocking(np[0]);
-      blocking(np[1]);
-      /* Start disorder-normalize */
-      if(!(npid = xfork())) {
-       if(!xfork()) {
-         /* Connect to the speaker process */
-         memset(&addr, 0, sizeof addr);
-         addr.sun_family = AF_UNIX;
-         snprintf(addr.sun_path, sizeof addr.sun_path,
-                  "%s/speaker/socket", config->home);
-         sfd = xsocket(PF_UNIX, SOCK_STREAM, 0);
-         if(connect(sfd, (const struct sockaddr *)&addr, sizeof addr) < 0)
-           fatal(errno, "connecting to %s", addr.sun_path);
-         l = strlen(q->id);
-         if(write(sfd, &l, sizeof l) < 0
-            || write(sfd, q->id, l) < 0)
-           fatal(errno, "writing to %s", addr.sun_path);
-         /* Await the ack */
-         if (read(sfd, &l, 1) < 0) 
-               fatal(errno, "reading ack from %s", addr.sun_path);
-         /* Plumbing */
-         xdup2(np[0], 0);
-         xdup2(sfd, 1);
-         xclose(np[0]);
-         xclose(np[1]);
-         xclose(sfd);
-         /* Ask the speaker to actually start playing the track; we do it here
-          * so it's definitely after ack. */
-         if(!prepare_only) {
-           strcpy(sm.id, q->id);
-           sm.type = SM_PLAY;
-           speaker_send(speaker_fd, &sm);
-           D(("sent SM_PLAY for %s", sm.id));
-         }
-         /* TODO stderr shouldn't be redirected for disorder-normalize
-          * (but it should be for play_track() */
-         execlp("disorder-normalize", "disorder-normalize",
-                log_default == &log_syslog ? "--syslog" : "--no-syslog",
-                "--config", configfile,
-                (char *)0);
-         fatal(errno, "executing disorder-normalize");
-         /* end of the innermost fork */
-       }
-       _exit(0);
-       /* end of the middle fork */
-      }
-      /* Wait for the middle fork to finish */
-      while(waitpid(npid, &n, 0) < 0 && errno == EINTR)
-       ;
-      /* Pass the file descriptor to the driver in an environment
-       * variable. */
-      snprintf(buffer, sizeof buffer, "DISORDER_RAW_FD=%d", np[1]);
-      if(putenv(buffer) < 0)
-       fatal(errno, "error calling putenv");
-      /* Close all the FDs we don't need */
-      xclose(np[0]);
-    }
-    if(waitdevice) {
-      ao_initialize();
-      if(*waitdevice) {
-       n = ao_driver_id(waitdevice);
-       if(n == -1)
-         fatal(0, "invalid libao driver: %s", optv[0]);
-       } else
-         n = ao_default_driver_id();
-      /* Make up a format. */
-      memset(&format, 0, sizeof format);
-      format.bits = 8;
-      format.rate = 44100;
-      format.channels = 1;
-      format.byte_format = AO_FMT_NATIVE;
-      retries = 20;
-      ts.tv_sec = 0;
-      ts.tv_nsec = 100000000;  /* 0.1s */
-      while((device = ao_open_live(n, &format, 0)) == 0 && retries-- > 0)
-         nanosleep(&ts, 0);
-      if(device)
-       ao_close(device);
-    }
-    play_track(q->pl,
-              optv, optc,
-              p,
-              q->track);
-    _exit(0);
-  case -1:                     /* error */
-    error(errno, "error calling fork");
-    if(q->type & DISORDER_PLAYER_PREFORK)
-      play_cleanup(q->pl, q->data);    /* else would leak */
-    if(lfd != -1)
-      xclose(lfd);
-    return START_SOFTFAIL;
+}
+
+/** @brief Child-process half of start()
+ * @return Process exit code
+ *
+ * Called in subprocess to execute non-raw-format players (via plugin).
+ */
+static int start_child(struct queue_entry *q, 
+                       const struct pbgc_params *params,
+                       void attribute((unused)) *bgdata) {
+  int n;
+
+  /* Wait for a device to clear.  This ugliness is now deprecated and will
+   * eventually be removed. */
+  if(params->waitdevice) {
+    ao_initialize();
+    if(*params->waitdevice) {
+      n = ao_driver_id(params->waitdevice);
+      if(n == -1)
+        disorder_fatal(0, "invalid libao driver: %s", params->waitdevice);
+    } else
+      n = ao_default_driver_id();
+    /* Make up a format. */
+    ao_sample_format format;
+    memset(&format, 0, sizeof format);
+    format.bits = 8;
+    format.rate = 44100;
+    format.channels = 1;
+    format.byte_format = AO_FMT_NATIVE;
+    int retries = 20;
+    struct timespec ts;
+    ts.tv_sec = 0;
+    ts.tv_nsec = 100000000;             /* 0.1s */
+    ao_device *device;
+    while((device = ao_open_live(n, &format, 0)) == 0 && retries-- > 0)
+      nanosleep(&ts, 0);
+    if(device)
+      ao_close(device);
   }
-  store_player_pid(q->id, pid);
-  q->prepared = 1;
-  if(lfd != -1)
-    xclose(lfd);
-  setpgid(pid, pid);
-  ev_child(ev, pid, 0, player_finished, q);
-  D(("player subprocess ID %lu", (unsigned long)pid));
-  return START_OK;
+  /* Play the track */
+  play_track(q->pl,
+             params->argv, params->argc,
+             params->rawpath,
+             q->track);
+  return 0;
 }
 
+/** @brief Prepare a track for later play
+ * @return @ref START_OK, @ref START_HARDFAIL or @ref START_SOFTFAIL
+ *
+ * This can be called either when we want to play the track or slightly before
+ * so that some samples are decoded and available in a buffer.
+ *
+ * Only applies to raw-format (i.e. speaker-using) players; everything else
+ * gets @c START_OK.
+ */
 int prepare(ev_source *ev,
            struct queue_entry *q) {
-  int n;
+  const struct stringlist *player;
 
+  /* If there's a decoder (or player!) going we do nothing */
+  if(q->pid >= 0)
+    return START_OK;
+  /* If the track is already prepared, do nothing */
+  if(q->prepared || q->preparing)
+    return START_OK;
   /* Find the player plugin */
-  if(find_player_pid(q->id) > 0) return 0; /* Already going. */
-  if((n = find_player(q)) < 0) return -1; /* No player */
-  q->pl = open_plugin(config->player.s[n].s[1], 0); /* No player */
+  if(!(player = find_player(q)) < 0) 
+    return START_HARDFAIL;              /* No player */
+  q->pl = open_plugin(player->s[1], 0);
   q->type = play_get_type(q->pl);
   if((q->type & DISORDER_PLAYER_TYPEMASK) != DISORDER_PLAYER_RAW)
-    return 0;                          /* Not a raw player */
-  return start(ev, q, 1/*prepare_only*/); /* Prepare it */
+    return START_OK;                    /* Not a raw player */
+  int rc = play_background(ev, player, q, prepare_child, NULL);
+  if(rc == START_OK) {
+    ev_child(ev, q->pid, 0, player_finished, q);
+    q->preparing = 1;
+    /* Actually the track is still "in flight" */
+    rc = START_SOFTFAIL;
+  }
+  return rc;
 }
 
+/** @brief Child-process half of prepare()
+ * @return Process exit code
+ *
+ * Called in subprocess to execute the decoder for a raw-format player.
+ *
+ * @todo We currently run the normalizer from here in a double-fork.  This is
+ * unsatisfactory for many reasons: we can't prevent it outliving the main
+ * server and we don't adequately report its exit status.
+ */
+static int prepare_child(struct queue_entry *q, 
+                         const struct pbgc_params *params,
+                         void attribute((unused)) *bgdata) {
+  /* np will be the pipe to disorder-normalize */
+  int np[2];
+  if(socketpair(PF_UNIX, SOCK_STREAM, 0, np) < 0)
+    disorder_fatal(errno, "error calling socketpair");
+  /* Beware of the Leopard!  On OS X 10.5.x, the order of the shutdown
+   * calls here DOES MATTER.  If you do the SHUT_WR first then the SHUT_RD
+   * fails with "Socket is not connected".  I think this is a bug but
+   * provided implementors either don't care about the order or all agree
+   * about the order, choosing the reliable order is an adequate
+   * workaround.  */
+  xshutdown(np[1], SHUT_RD);   /* decoder writes to np[1] */
+  xshutdown(np[0], SHUT_WR);   /* normalize reads from np[0] */
+  blocking(np[0]);
+  blocking(np[1]);
+  /* Start disorder-normalize.  We double-fork so that nothing has to wait
+   * for disorder-normalize. */
+  pid_t npid;
+  if(!(npid = xfork())) {
+    /* Grandchild of disorderd */
+    if(!xfork()) {
+      /* Great-grandchild of disorderd */
+      /* Connect to the speaker process */
+      struct sockaddr_un addr;
+      memset(&addr, 0, sizeof addr);
+      addr.sun_family = AF_UNIX;
+      snprintf(addr.sun_path, sizeof addr.sun_path,
+               "%s/speaker/socket", config->home);
+      int sfd = xsocket(PF_UNIX, SOCK_STREAM, 0);
+      if(connect(sfd, (const struct sockaddr *)&addr, sizeof addr) < 0)
+        disorder_fatal(errno, "connecting to %s", addr.sun_path);
+      /* Send the ID, with a NATIVE-ENDIAN 32 bit length */
+      uint32_t l = strlen(q->id);
+      if(write(sfd, &l, sizeof l) < 0
+         || write(sfd, q->id, l) < 0)
+        disorder_fatal(errno, "writing to %s", addr.sun_path);
+      /* Await the ack */
+      if (read(sfd, &l, 1) < 0) 
+        disorder_fatal(errno, "reading ack from %s", addr.sun_path);
+      /* Plumbing */
+      xdup2(np[0], 0);
+      xdup2(sfd, 1);
+      xclose(np[0]);
+      xclose(np[1]);
+      xclose(sfd);
+      /* TODO stderr shouldn't be redirected for disorder-normalize
+       * (but it should be for play_track() */
+      execlp("disorder-normalize", "disorder-normalize",
+             log_default == &log_syslog ? "--syslog" : "--no-syslog",
+             "--config", configfile,
+             (char *)0);
+      disorder_fatal(errno, "executing disorder-normalize");
+      /* End of the great-grandchild of disorderd */
+    }
+    /* Back in the grandchild of disorderd */
+    _exit(0);
+    /* End of the grandchild of disorderd */
+  }
+  /* Back in the child of disorderd */
+  /* Wait for the grandchild of disordered to finish */
+  int n;
+  while(waitpid(npid, &n, 0) < 0 && errno == EINTR)
+    ;
+  /* Pass the file descriptor to the driver in an environment
+   * variable. */
+  char buffer[64];
+  snprintf(buffer, sizeof buffer, "DISORDER_RAW_FD=%d", np[1]);
+  if(putenv(buffer) < 0)
+    disorder_fatal(errno, "error calling putenv");
+  /* Close all the FDs we don't need */
+  xclose(np[0]);
+  /* Start the decoder itself */
+  play_track(q->pl,
+             params->argv, params->argc,
+             params->rawpath,
+             q->track);
+  return 0;
+}
+
+/** @brief Abandon a queue entry
+ *
+ * Called from c_remove() (but NOT when scratching a track).  Only does
+ * anything to raw-format tracks.  Terminates the background decoder and tells
+ * the speaker process to cancel the track.
+ */
 void abandon(ev_source attribute((unused)) *ev,
             struct queue_entry *q) {
   struct speaker_message sm;
-  pid_t pid = find_player_pid(q->id);
 
-  if(pid < 0) return;                  /* Not prepared. */
+  if(q->pid < 0)
+    return;                             /* Not prepared. */
   if((q->type & DISORDER_PLAYER_TYPEMASK) != DISORDER_PLAYER_RAW)
     return;                            /* Not a raw player. */
   /* Terminate the player. */
-  kill(-pid, config->signal);
-  forget_player_pid(q->id);
+  kill(-q->pid, config->signal);
   /* Cancel the track. */
   memset(&sm, 0, sizeof sm);
   sm.type = SM_CANCEL;
@@ -482,6 +524,8 @@ void abandon(ev_source attribute((unused)) *ev,
   speaker_send(speaker_fd, &sm);
 }
 
+/* Random tracks ------------------------------------------------------------ */
+
 /** @brief Called with a new random track
  * @param ev Event loop
  * @param track Track name
@@ -493,7 +537,7 @@ static void chosen_random_track(ev_source *ev,
   if(!track)
     return;
   /* Add the track to the queue */
-  q = queue_add(track, 0, WHERE_END, origin_random);
+  q = queue_add(track, 0, WHERE_END, NULL, origin_random);
   D(("picked %p (%s) at random", (void *)q, q->track));
   queue_write();
   /* Maybe a track can now be played */
@@ -502,6 +546,9 @@ static void chosen_random_track(ev_source *ev,
 
 /** @brief Maybe add a randomly chosen track
  * @param ev Event loop
+ *
+ * Picking can take some time so the track will only be added after this
+ * function has returned.
  */
 void add_random_track(ev_source *ev) {
   struct queue_entry *q;
@@ -518,12 +565,20 @@ void add_random_track(ev_source *ev) {
     trackdb_request_random(ev, chosen_random_track);
 }
 
-/* try to play a track */
+/* Track initiation (part 2) ------------------------------------------------ */
+
+/** @brief Attempt to play something
+ *
+ * This is called from numerous locations - whenever it might conceivably have
+ * become possible to play something.
+ */
 void play(ev_source *ev) {
   struct queue_entry *q;
   int random_enabled = random_is_enabled();
 
   D(("play playing=%p", (void *)playing));
+  /* If we're shutting down, or there's something playing, or playing is not
+   * enabled, give up now */
   if(shutting_down || playing || !playing_is_enabled()) return;
   /* See if there's anything to play */
   if(qhead.next == &qhead) {
@@ -531,6 +586,8 @@ void play(ev_source *ev) {
      * attempts to add a random track anyway.  However they are rarer than
      * attempts to force a track so we initiate one now. */
     add_random_track(ev);
+    /* chosen_random_track() will call play() when a new random track has been
+     * added to the queue. */
     return;
   }
   /* There must be at least one track in the queue. */
@@ -542,7 +599,7 @@ void play(ev_source *ev) {
     return;
   D(("taken %p (%s) from queue", (void *)q, q->track));
   /* Try to start playing. */
-  switch(start(ev, q, 0/*!prepare_only*/)) {
+  switch(start(ev, q)) {
   case START_HARDFAIL:
     if(q == qhead.next) {
       queue_remove(q, 0);              /* Abandon this track. */
@@ -556,12 +613,14 @@ void play(ev_source *ev) {
     /* We'll try the same track again shortly. */
     break;
   case START_OK:
+    /* Remove from the queue */
     if(q == qhead.next) {
       queue_remove(q, 0);
       queue_write();
     }
+    /* It's become the playing track */
     playing = q;
-    time(&playing->played);
+    xtime(&playing->played);
     playing->state = playing_started;
     notify_play(playing->track, playing->submitter);
     eventlog("playing", playing->track,
@@ -573,16 +632,22 @@ void play(ev_source *ev) {
      * potentially be a just-added random track. */
     if(qhead.next != &qhead)
       prepare(ev, qhead.next);
+    /* Make sure there is a prepared scratch */
+    ensure_next_scratch(ev);
     break;
   }
 }
 
+/* Miscelleneous ------------------------------------------------------------ */
+
+/** @brief Return true if play is enabled */
 int playing_is_enabled(void) {
   const char *s = trackdb_get_global("playing");
 
   return !s || !strcmp(s, "yes");
 }
 
+/** @brief Enable play */
 void enable_playing(const char *who, ev_source *ev) {
   trackdb_set_global("playing", "yes", who);
   /* Add a random track if necessary. */
@@ -590,49 +655,76 @@ void enable_playing(const char *who, ev_source *ev) {
   play(ev);
 }
 
+/** @brief Disable play */
 void disable_playing(const char *who) {
   trackdb_set_global("playing", "no", who);
 }
 
+/** @brief Return true if random play is enabled */
 int random_is_enabled(void) {
   const char *s = trackdb_get_global("random-play");
 
   return !s || !strcmp(s, "yes");
 }
 
+/** @brief Enable random play */
 void enable_random(const char *who, ev_source *ev) {
   trackdb_set_global("random-play", "yes", who);
   add_random_track(ev);
   play(ev);
 }
 
+/** @brief Disable random play */
 void disable_random(const char *who) {
   trackdb_set_global("random-play", "no", who);
 }
 
+/* Scratching --------------------------------------------------------------- */
+
+/** @brief Track to play next time something is scratched */
+static struct queue_entry *next_scratch;
+
+/** @brief Ensure there isa prepared scratch */
+static void ensure_next_scratch(ev_source *ev) {
+  if(next_scratch)                      /* There's one already */
+    return;
+  if(!config->scratch.n)                /* There are no scratches */
+    return;
+  int r = rand() * (double)config->scratch.n / (RAND_MAX + 1.0);
+  next_scratch = queue_add(config->scratch.s[r], NULL,
+                           WHERE_NOWHERE, NULL, origin_scratch);
+  if(ev)
+    prepare(ev, next_scratch);
+}
+
+/** @brief Scratch a track
+ * @param who User responsible (or NULL)
+ * @param id Track ID (or NULL for current)
+ */
 void scratch(const char *who, const char *id) {
-  struct queue_entry *q;
   struct speaker_message sm;
-  pid_t pid;
 
   D(("scratch playing=%p state=%d id=%s playing->id=%s",
      (void *)playing,
      playing ? playing->state : 0,
      id ? id : "(none)",
      playing ? playing->id : "(none)"));
+  /* There must be a playing track; it must be in a scratchable state; if a
+   * specific ID was mentioned it must be that track. */
   if(playing
      && (playing->state == playing_started
         || playing->state == playing_paused)
      && (!id
         || !strcmp(id, playing->id))) {
+    /* Update state (for the benefit of the 'recent' list) */
     playing->state = playing_scratched;
     playing->scratched = who ? xstrdup(who) : 0;
-    if((pid = find_player_pid(playing->id)) > 0) {
-      D(("kill -%d %lu", config->signal, (unsigned long)pid));
-      kill(-pid, config->signal);
-      forget_player_pid(playing->id);
-    } else
-      error(0, "could not find PID for %s", playing->id);
+    /* Find the player and kill the whole process group */
+    if(playing->pid >= 0) {
+      D(("kill -%d -%lu", config->signal, (unsigned long)playing->pid));
+      kill(-playing->pid, config->signal);
+    }
+    /* Tell the speaker, if we think it'll care */
     if((playing->type & DISORDER_PLAYER_TYPEMASK) == DISORDER_PLAYER_RAW) {
       memset(&sm, 0, sizeof sm);
       sm.type = SM_CANCEL;
@@ -640,46 +732,55 @@ void scratch(const char *who, const char *id) {
       speaker_send(speaker_fd, &sm);
       D(("sending SM_CANCEL for %s", playing->id));
     }
-    /* put a scratch track onto the front of the queue (but don't
-     * bother if playing is disabled) */
-    if(playing_is_enabled() && config->scratch.n) {
-      int r = rand() * (double)config->scratch.n / (RAND_MAX + 1.0);
-      q = queue_add(config->scratch.s[r], who, WHERE_START, origin_scratch);
+    /* If playing is enabled then add a scratch to the queue.  Having a scratch
+     * appear in the queue when further play is disabled is weird and
+     * contradicts implicit assumptions made elsewhere, so we try to avoid
+     * it. */
+    if(playing_is_enabled()) {
+      /* Try to make sure there is a scratch */
+      ensure_next_scratch(NULL);
+      /* Insert it at the head of the queue */
+      if(next_scratch){
+        next_scratch->submitter = who;
+        queue_insert_entry(&qhead, next_scratch);
+        eventlog_raw("queue", queue_marshall(next_scratch), (const char *)0);
+        next_scratch = NULL;
+      }
     }
     notify_scratch(playing->track, playing->submitter, who,
-                  time(0) - playing->played);
+                  xtime(0) - playing->played);
   }
 }
 
+/* Server termination ------------------------------------------------------- */
+
+/** @brief Called from quit() to tear down everything belonging to this file */
 void quitting(ev_source *ev) {
   struct queue_entry *q;
-  pid_t pid;
 
   /* Don't start anything new */
   shutting_down = 1;
   /* Shut down the current player */
   if(playing) {
-    if((pid = find_player_pid(playing->id)) > 0) {
-      kill(-pid, config->signal);
-      forget_player_pid(playing->id);
-    } else
-      error(0, "could not find PID for %s", playing->id);
+    if(playing->pid >= 0)
+      kill(-playing->pid, config->signal);
     playing->state = playing_quitting;
     finished(0);
   }
-  /* Zap any other players */
+  /* Zap any background decoders that are going */
   for(q = qhead.next; q != &qhead; q = q->next)
-    if((pid = find_player_pid(q->id)) > 0) {
-      D(("kill -%d %lu", config->signal, (unsigned long)pid));
-      kill(-pid, config->signal);
-      forget_player_pid(q->id);
-    } else
-      error(0, "could not find PID for %s", q->id);
+    if(q->pid >= 0) {
+      D(("kill -%d %lu", config->signal, (unsigned long)q->pid));
+      kill(-q->pid, config->signal);
+    }
   /* Don't need the speaker any more */
   ev_fd_cancel(ev, ev_read, speaker_fd);
   xclose(speaker_fd);
 }
 
+/* Pause and resume --------------------------------------------------------- */
+
+/** @brief Pause the playing track */
 int pause_playing(const char *who) {
   struct speaker_message sm;
   long played;
@@ -690,14 +791,14 @@ int pause_playing(const char *who) {
   case DISORDER_PLAYER_STANDALONE:
     if(!(playing->type & DISORDER_PLAYER_PAUSES)) {
     default:
-      error(0,  "cannot pause because player is not powerful enough");
+      disorder_error(0,  "cannot pause because player is not powerful enough");
       return -1;
     }
     if(play_pause(playing->pl, &played, playing->data)) {
-      error(0, "player indicates it cannot pause");
+      disorder_error(0, "player indicates it cannot pause");
       return -1;
     }
-    time(&playing->lastpaused);
+    xtime(&playing->lastpaused);
     playing->uptopause = played;
     playing->lastresumed = 0;
     break;
@@ -707,7 +808,8 @@ int pause_playing(const char *who) {
     speaker_send(speaker_fd, &sm);
     break;
   }
-  if(who) info("paused by %s", who);
+  if(who)
+    disorder_info("paused by %s", who);
   notify_pause(playing->track, who);
   paused = 1;
   if(playing->state == playing_started)
@@ -716,6 +818,7 @@ int pause_playing(const char *who) {
   return 0;
 }
 
+/** @brief Resume playing after a pause */
 void resume_playing(const char *who) {
   struct speaker_message sm;
 
@@ -724,13 +827,13 @@ void resume_playing(const char *who) {
   if(!playing) return;
   switch(playing->type & DISORDER_PLAYER_TYPEMASK) {
   case DISORDER_PLAYER_STANDALONE:
-    if(!playing->type & DISORDER_PLAYER_PAUSES) {
+    if(!(playing->type & DISORDER_PLAYER_PAUSES)) {
     default:
       /* Shouldn't happen */
       return;
     }
     play_resume(playing->pl, playing->data);
-    time(&playing->lastresumed);
+    xtime(&playing->lastresumed);
     break;
   case DISORDER_PLAYER_RAW:
     memset(&sm, 0, sizeof sm);
@@ -738,7 +841,7 @@ void resume_playing(const char *who) {
     speaker_send(speaker_fd, &sm);
     break;
   }
-  if(who) info("resumed by %s", who);
+  if(who) disorder_info("resumed by %s", who);
   notify_resume(playing->track, who);
   if(playing->state == playing_paused)
     playing->state = playing_started;
@@ -750,5 +853,6 @@ Local Variables:
 c-basic-offset:2
 comment-column:40
 fill-column:79
+indent-tabs-mode:nil
 End:
 */
index 6695d5a..7afbde9 100644 (file)
@@ -54,7 +54,7 @@ const struct plugin *open_plugin(const char *name,
     if(access(p, R_OK) == 0) {
       h = dlopen(p, RTLD_NOW);
       if(!h) {
-       error(0, "error opening %s: %s", p, dlerror());
+       disorder_error(0, "error opening %s: %s", p, dlerror());
        continue;
       }
       pl = xmalloc(sizeof *pl);
@@ -65,7 +65,8 @@ const struct plugin *open_plugin(const char *name,
       return pl;
     }
   }
-  (flags & PLUGIN_FATAL ? fatal : error)(0, "cannot find plugin '%s'", name);
+  (flags & PLUGIN_FATAL ? disorder_fatal : disorder_error)
+    (0, "cannot find plugin '%s'", name);
   return 0;
 }
 
@@ -75,8 +76,8 @@ function_t *get_plugin_function(const struct plugin *pl,
 
   f = (function_t *)dlsym(pl->dlhandle, symbol);
   if(!f)
-    fatal(0, "error looking up function '%s' in '%s': %s",
-         symbol, pl->name, dlerror());
+    disorder_fatal(0, "error looking up function '%s' in '%s': %s",
+                  symbol, pl->name, dlerror());
   return f;
 }
 
@@ -86,8 +87,8 @@ const void *get_plugin_object(const struct plugin *pl,
 
   o = dlsym(pl->dlhandle, symbol);
   if(!o)
-    fatal(0, "error looking up object '%s' in '%s': %s",
-         symbol, pl->name, dlerror());
+    disorder_fatal(0, "error looking up object '%s' in '%s': %s",
+                  symbol, pl->name, dlerror());
   return o;
 }
 
index 7dfcdb1..7dcaa84 100644 (file)
@@ -48,17 +48,32 @@ static void queue_id(struct queue_entry *q) {
   q->id = id;
 }
 
+/** @brief Add a track to the queue
+ * @param track Track to add
+ * @param submitter Who added it, or NULL
+ * @param where Where to add it
+ * @param target ID to add after for @ref WHERE_AFTER
+ * @param origin Track origin
+ * @return New queue entry or NULL
+ *
+ * The queue is NOT saved to disk.
+ *
+ * NULL can only be returned if @ref WHERE_AFTER is used with an invalid
+ * queue ID.
+ */
 struct queue_entry *queue_add(const char *track, const char *submitter,
-                             int where, enum track_origin origin) {
-  struct queue_entry *q, *beforeme;
+                             int where, const char *target,
+                              enum track_origin origin) {
+  struct queue_entry *q, *beforeme, *afterme;
 
   q = xmalloc(sizeof *q);
   q->track = xstrdup(track);
   q->submitter = submitter ? xstrdup(submitter) : 0;
   q->state = playing_unplayed;
   q->origin = origin;
+  q->pid = -1;
   queue_id(q);
-  time(&q->when);
+  xtime(&q->when);
   switch(where) {
   case WHERE_START:
     queue_insert_entry(&qhead, q);
@@ -75,6 +90,22 @@ struct queue_entry *queue_add(const char *track, const char *submitter,
       beforeme = beforeme->prev;
     queue_insert_entry(beforeme->prev, q);
     break;
+  case WHERE_AFTER:
+    if(!*target)
+      /* Insert at start of queue */
+      afterme = &qhead;
+    else {
+      /* Insert after a specific track */
+      afterme = qhead.next;
+      while(afterme != &qhead && strcmp(afterme->id, target))
+        afterme = afterme->next;
+      if(afterme == &qhead)
+        return NULL;
+    }
+    queue_insert_entry(afterme, q);
+    break;
+  case WHERE_NOWHERE:
+    return q;
   }
   /* submitter will be a null pointer for a scratch */
   if(submitter)
@@ -125,7 +156,7 @@ int queue_move(struct queue_entry *q, int delta, const char *who) {
   }
 
   if(moved) {
-    info("user %s moved %s", who, q->id);
+    disorder_info("user %s moved %s", who, q->id);
     notify_queue_move(q->track, who);
     sprintf(buffer, "%d", moved);
     eventlog("moved", who, (char *)0);
@@ -153,7 +184,7 @@ void queue_moveafter(struct queue_entry *target,
     queue_insert_entry(target, q);
     target = q;
     /* Log the individual tracks */
-    info("user %s moved %s", who, q->id);
+    disorder_info("user %s moved %s", who, q->id);
     notify_queue_move(q->track, who);
   }
   /* Report that the queue changed to the event log */
@@ -162,7 +193,7 @@ void queue_moveafter(struct queue_entry *target,
 
 void queue_remove(struct queue_entry *which, const char *who) {
   if(who) {
-    info("user %s removed %s", who, which->id);
+    disorder_info("user %s removed %s", who, which->id);
     notify_queue_move(which->track, who);
   }
   eventlog("removed", which->id, who, (const char *)0);
index 1380e24..bc99681 100644 (file)
@@ -70,12 +70,12 @@ static int aborted(void) {
 /* Exit if our parent has gone away or we have been told to stop. */
 static void checkabort(void) {
   if(getppid() == 1) {
-    info("parent has terminated");
+    disorder_info("parent has terminated");
     trackdb_abort_transaction(global_tid);
     exit(0);
   }
   if(signalled) {
-    info("received signal %d", signalled);
+    disorder_info("received signal %d", signalled);
     trackdb_abort_transaction(global_tid);
     exit(0);
   }
@@ -90,7 +90,7 @@ static void rescan_collection(const struct collection *c) {
   long ntracks = 0, nnew = 0;
   
   checkabort();
-  info("rescanning %s with %s", c->root, c->module);
+  disorder_info("rescanning %s with %s", c->root, c->module);
   /* plugin runs in a subprocess */
   xpipe(p);
   if(!(pid = xfork())) {
@@ -100,29 +100,29 @@ static void rescan_collection(const struct collection *c) {
     xclose(p[1]);
     scan(c->module, c->root);
     if(fflush(stdout) < 0)
-      fatal(errno, "error writing to scanner pipe");
+      disorder_fatal(errno, "error writing to scanner pipe");
     _exit(0);
   }
   xclose(p[1]);
   if(!(fp = fdopen(p[0], "r")))
-    fatal(errno, "error calling fdopen");
+    disorder_fatal(errno, "error calling fdopen");
   /* read tracks from the plugin */
   while(!inputline("rescanner", fp, &path, 0)) {
     checkabort();
     /* actually we can cope relatively well within the server, but they'll go
      * wrong in track listings */
     if(strchr(path, '\n')) {
-      error(0, "cannot cope with tracks with newlines in the name");
+      disorder_error(0, "cannot cope with tracks with newlines in the name");
       continue;
     }
     if(!(track = any2utf8(c->encoding, path))) {
-      error(0, "cannot convert track path to UTF-8: %s", path);
+      disorder_error(0, "cannot convert track path to UTF-8: %s", path);
       continue;
     }
     if(config->dbversion > 1) {
       /* We use NFC track names */
       if(!(track = utf8_compose_canon(track, strlen(track), 0))) {
-        error(0, "cannot convert track path to NFC: %s", path);
+        disorder_error(0, "cannot convert track path to NFC: %s", path);
         continue;
       }
     }
@@ -134,28 +134,28 @@ static void rescan_collection(const struct collection *c) {
     if(n < config->player.n) {
       nnew += !!trackdb_notice(track, path);
       ++ntracks;
-      if(ntracks % 100 == 0 && time(0) > last_report + 10) {
-        info("rescanning %s, %ld tracks so far", c->root, ntracks);
-        time(&last_report);
+      if(ntracks % 100 == 0 && xtime(0) > last_report + 10) {
+        disorder_info("rescanning %s, %ld tracks so far", c->root, ntracks);
+        xtime(&last_report);
       }
     }
   }
   /* tidy up */
   if(ferror(fp)) {
-    error(errno, "error reading from scanner pipe");
+    disorder_error(errno, "error reading from scanner pipe");
     goto done;
   }
   xfclose(fp);
   fp = 0;
   while((r = waitpid(pid, &w, 0)) == -1 && errno == EINTR)
     ;
-  if(r < 0) fatal(errno, "error calling waitpid");
+  if(r < 0) disorder_fatal(errno, "error calling waitpid");
   pid = 0;
   if(w) {
-    error(0, "scanner subprocess: %s", wstat(w));
+    disorder_error(0, "scanner subprocess: %s", wstat(w));
     goto done;
   }
-  info("rescanned %s, %ld tracks, %ld new", c->root, ntracks, nnew);
+  disorder_info("rescanned %s, %ld tracks, %ld new", c->root, ntracks, nnew);
 done:
   if(fp)
     xfclose(fp);
@@ -229,7 +229,7 @@ static int recheck_track_tid(struct recheck_state *cs,
       if(fnmatch(config->tracklength.s[n].s[0], t->track, 0) == 0)
         break;
     if(n >= config->tracklength.n)
-      error(0, "no tracklength plugin found for %s", t->track);
+      disorder_error(0, "no tracklength plugin found for %s", t->track);
     else {
       length = tracklength(config->tracklength.s[n].s[1], t->track, path);
       if(length > 0) {
@@ -259,9 +259,9 @@ static void recheck_collection(const struct collection *c) {
   long nrc;
 
   if(c)
-    info("rechecking %s", c->root);
+    disorder_info("rechecking %s", c->root);
   else
-    info("rechecking all tracks");
+    disorder_info("rechecking all tracks");
   /* Doing the checking inside a transaction locks up the server for much too
    * long (because it spends lots of time thinking about each track).  So we
    * pull the full track list into memory and work from that.
@@ -271,7 +271,7 @@ static void recheck_collection(const struct collection *c) {
    */
   for(;;) {
     checkabort();
-    info("getting track list");
+    disorder_info("getting track list");
     global_tid = trackdb_begin_transaction();
     memset(&cs, 0, sizeof cs);
     cs.c = c;
@@ -288,9 +288,9 @@ static void recheck_collection(const struct collection *c) {
     sleep(10);
     checkabort();
     if(c)
-      info("resuming recheck of %s", c->root);
+      disorder_info("resuming recheck of %s", c->root);
     else
-      info("resuming global recheck");
+      disorder_info("resuming global recheck");
   }
   trackdb_commit_transaction(global_tid);
   global_tid = 0;
@@ -300,19 +300,19 @@ static void recheck_collection(const struct collection *c) {
       return;
     recheck_track(&cs, t);
     ++nrc;
-    if(nrc % 100 == 0 && time(0) > last_report + 10) {
+    if(nrc % 100 == 0 && xtime(0) > last_report + 10) {
       if(c)
-        info("rechecking %s, %ld tracks so far", c->root, nrc);
+        disorder_info("rechecking %s, %ld tracks so far", c->root, nrc);
       else
-        info("rechecking all tracks, %ld tracks so far", nrc);
-      time(&last_report);
+        disorder_info("rechecking all tracks, %ld tracks so far", nrc);
+      xtime(&last_report);
     }
   }
   if(c)
-    info("rechecked %s, %ld obsoleted, %ld lengths calculated",
-         c->root, cs.nobsolete, cs.nlength);
+    disorder_info("rechecked %s, %ld obsoleted, %ld lengths calculated",
+                  c->root, cs.nobsolete, cs.nlength);
   else
-    info("rechecked all tracks, %ld no collection, %ld obsoleted, %ld lengths calculated",
+    disorder_info("rechecked all tracks, %ld no collection, %ld obsoleted, %ld lengths calculated",
          cs.nnocollection, cs.nobsolete, cs.nlength);
 }
 
@@ -327,7 +327,7 @@ static void do_directory(const char *s,
   if(n < config->collection.n)
     fn(&config->collection.s[n]);
   else
-    error(0, "no collection has root '%s'", s);
+    disorder_error(0, "no collection has root '%s'", s);
 }
 
 /* rescan/recheck all collections */
@@ -350,7 +350,7 @@ static void do_all(void (*fn)(const struct collection *c)) {
 static void expire_noticed(void) {
   time_t now;
 
-  time(&now);
+  xtime(&now);
   trackdb_expire_noticed(now - config->noticed_history * 86400);
 }
 
@@ -361,7 +361,7 @@ int main(int argc, char **argv) {
   
   set_progname(argv);
   mem_init();
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "hVc:dDSsKC", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
@@ -373,21 +373,21 @@ int main(int argc, char **argv) {
     case 's': logsyslog = 1; break;
     case 'K': do_check = 1; break;
     case 'C': do_check = 0; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   if(logsyslog) {
     openlog(progname, LOG_PID, LOG_DAEMON);
     log_default = &log_syslog;
   }
-  if(config_read(0)) fatal(0, "cannot read configuration");
+  if(config_read(0, NULL)) disorder_fatal(0, "cannot read configuration");
   xnice(config->nice_rescan);
   sa.sa_handler = signal_handler;
   sa.sa_flags = SA_RESTART;
   sigemptyset(&sa.sa_mask);
   xsigaction(SIGTERM, &sa, 0);
   xsigaction(SIGINT, &sa, 0);
-  info("started");
+  disorder_info("started");
   trackdb_init(TRACKDB_NO_RECOVER);
   trackdb_open(TRACKDB_NO_UPGRADE);
   if(optind == argc) {
@@ -409,8 +409,8 @@ int main(int argc, char **argv) {
         do_directory(argv[n], recheck_collection);
   }
   trackdb_close();
-  trackdb_deinit();
-  info("completed");
+  trackdb_deinit(NULL);
+  disorder_info("completed");
   return 0;
 }
 
index ec7eddc..f1b20a1 100644 (file)
@@ -114,7 +114,8 @@ static int schedule_parse(const DBT *k,
 
   /* Reject bogus keys */
   if(!k->size || k->size > 128) {
-    error(0, "bogus schedule.db key (%lu bytes)", (unsigned long)k->size);
+    disorder_error(0, "bogus schedule.db key (%lu bytes)",
+                   (unsigned long)k->size);
     return -1;
   }
   id = xstrndup(k->data, k->size);
@@ -122,8 +123,8 @@ static int schedule_parse(const DBT *k,
   /* Reject items without the required fields */
   for(n = 0; n < NREQUIRED; ++n) {
     if(!kvp_get(actiondata, schedule_required[n])) {
-      error(0, "scheduled event %s: missing required field '%s'",
-           id, schedule_required[n]);
+      disorder_error(0, "scheduled event %s: missing required field '%s'",
+                     id, schedule_required[n]);
       return -1;
     }
   }
@@ -146,10 +147,10 @@ static int cdel(DBC *cursor) {
   case 0:
     break;
   case DB_LOCK_DEADLOCK:
-    error(0, "error deleting from schedule.db: %s", db_strerror(err));
+    disorder_error(0, "error deleting from schedule.db: %s", db_strerror(err));
     break;
   default:
-    fatal(0, "error deleting from schedule.db: %s", db_strerror(err));
+    disorder_fatal(0, "error deleting from schedule.db: %s", db_strerror(err));
   }
   return err;
 }
@@ -182,13 +183,13 @@ static int schedule_init_tid(ev_source *ev,
     }
     when.tv_usec = 0;
     /* The action might be in the past */
-    if(when.tv_sec < time(0)) {
+    if(when.tv_sec < xtime(0)) {
       const char *priority = kvp_get(actiondata, "priority");
 
       if(priority && !strcmp(priority, "junk")) {
         /* Junk actions that are in the past are discarded during startup */
        /* TODO recurring events should be handled differently here */
-        info("junk event %s is in the past, discarding", id);
+        disorder_info("junk event %s is in the past, discarding", id);
        if(cdel(cursor))
          goto deadlocked;
         /* Skip this time */
@@ -203,10 +204,10 @@ static int schedule_init_tid(ev_source *ev,
     err = 0;
     break;
   case DB_LOCK_DEADLOCK:
-    error(0, "error querying schedule.db: %s", db_strerror(err));
+    disorder_error(0, "error querying schedule.db: %s", db_strerror(err));
     break;
   default:
-    fatal(0, "error querying schedule.db: %s", db_strerror(err));
+    disorder_fatal(0, "error querying schedule.db: %s", db_strerror(err));
   }
 deadlocked:
   if(trackdb_closecursor(cursor))
@@ -247,12 +248,12 @@ static int schedule_add_tid(const char *id,
   case 0:
     break;
   case DB_LOCK_DEADLOCK:
-    error(0, "error updating schedule.db: %s", db_strerror(err));
+    disorder_error(0, "error updating schedule.db: %s", db_strerror(err));
     return err;
   case DB_KEYEXIST:
     return err;
   default:
-    fatal(0, "error updating schedule.db: %s", db_strerror(err));
+    disorder_fatal(0, "error updating schedule.db: %s", db_strerror(err));
   }
   return 0;
 }
@@ -276,8 +277,8 @@ const char *schedule_add(ev_source *ev,
   /* Check that the required field are present */
   for(n = 0; n < NREQUIRED; ++n) {
     if(!kvp_get(actiondata, schedule_required[n])) {
-      error(0, "new scheduled event is missing required field '%s'",
-           schedule_required[n]);
+      disorder_error(0, "new scheduled event is missing required field '%s'",
+                     schedule_required[n]);
       return 0;
     }
   }
@@ -287,8 +288,8 @@ const char *schedule_add(ev_source *ev,
   when.tv_sec = atoll(kvp_get(actiondata, "when"));
   when.tv_usec = 0;
   /* Reject events in the past */
-  if(when.tv_sec <= time(0)) {
-    error(0, "new scheduled event is in the past");
+  if(when.tv_sec <= xtime(0)) {
+    disorder_error(0, "new scheduled event is in the past");
     return 0;
   }
   do {
@@ -313,8 +314,8 @@ struct kvp *schedule_get(const char *id) {
   /* Check that the required field are present */
   for(n = 0; n < NREQUIRED; ++n) {
     if(!kvp_get(actiondata, schedule_required[n])) {
-      error(0, "scheduled event %s is missing required field '%s'",
-           id, schedule_required[n]);
+      disorder_error(0, "scheduled event %s is missing required field '%s'",
+                     id, schedule_required[n]);
       return 0;
     }
   }
@@ -362,19 +363,19 @@ static void schedule_play(ev_source *ev,
 
   /* This stuff has rather a lot in common with c_play() */
   if(!track) {
-    error(0, "scheduled event %s: no track field", id);
+    disorder_error(0, "scheduled event %s: no track field", id);
     return;
   }
   if(!trackdb_exists(track)) {
-    error(0, "scheduled event %s: no such track as %s", id, track);
+    disorder_error(0, "scheduled event %s: no such track as %s", id, track);
     return;
   }
   if(!(track = trackdb_resolve(track))) {
-    error(0, "scheduled event %s: cannot resolve track %s", id, track);
+    disorder_error(0, "scheduled event %s: cannot resolve track %s", id, track);
     return;
   }
-  info("scheduled event %s: %s play %s", id,  who, track);
-  q = queue_add(track, who, WHERE_START, origin_scheduled);
+  disorder_info("scheduled event %s: %s play %s", id,  who, track);
+  q = queue_add(track, who, WHERE_START, NULL, origin_scheduled);
   queue_write();
   if(q == qhead.next && playing)
     prepare(ev, q);
@@ -389,18 +390,19 @@ static void schedule_set_global(ev_source attribute((unused)) *ev,
   const char *value = kvp_get(actiondata, "value");
 
   if(!key) {
-    error(0, "scheduled event %s: no key field", id);
+    disorder_error(0, "scheduled event %s: no key field", id);
     return;
   }
   if(key[0] == '_') {
-    error(0, "scheduled event %s: cannot set internal global preferences (%s)",
-         id, key);
+    disorder_error(0, "scheduled event %s: cannot set internal global preferences (%s)",
+                   id, key);
     return;
   }
   if(value)
-    info("scheduled event %s: %s set-global %s=%s", id, who, key, value);
+    disorder_info("scheduled event %s: %s set-global %s=%s",
+                  id, who, key, value);
   else
-    info("scheduled event %s: %s set-global %s unset", id,  who, key);
+    disorder_info("scheduled event %s: %s set-global %s unset", id,  who, key);
   trackdb_set_global(key, value, who);
 }
 
@@ -438,27 +440,28 @@ static int schedule_lookup(const char *id,
   /* Look up the action */
   n = TABLE_FIND(schedule_actions, name, action);
   if(n < 0) {
-    error(0, "scheduled event %s: unrecognized action '%s'", id, action);
+    disorder_error(0, "scheduled event %s: unrecognized action '%s'",
+                   id, action);
     return -1;
   }
   /* Find the user */
   if(!(userinfo = trackdb_getuserinfo(who))) {
-    error(0, "scheduled event %s: user '%s' does not exist", id, who);
+    disorder_error(0, "scheduled event %s: user '%s' does not exist", id, who);
     return -1;
   }
   /* Check that they have suitable rights */
   if(!(rights = kvp_get(userinfo, "rights"))) {
-    error(0, "scheduled event %s: user %s' has no rights???", id, who);
+    disorder_error(0, "scheduled event %s: user %s' has no rights???", id, who);
     return -1;
   }
   if(parse_rights(rights, &r, 1)) {
-    error(0, "scheduled event %s: user %s has invalid rights '%s'",
-         id, who, rights);
+    disorder_error(0, "scheduled event %s: user %s has invalid rights '%s'",
+                   id, who, rights);
     return -1;
   }
   if(!(r & schedule_actions[n].right)) {
-    error(0, "scheduled event %s: user %s lacks rights for action %s",
-         id, who, action);
+    disorder_error(0, "scheduled event %s: user %s lacks rights for action %s",
+                   id, who, action);
     return -1;
   }
   return n;
index 88e7723..25dae71 100644 (file)
@@ -15,6 +15,9 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
+/** @file server/server-queue.c
+ * @brief Server-specific track queue support
+ */
 #include "disorder-server.h"
 
 /* the head of the queue is played next, so normally we add to the tail */
@@ -47,18 +50,18 @@ void queue_fix_sofar(struct queue_entry *q) {
       if(q->uptopause == -1)           /* Don't know how far thru. */
        sofar = -1;
       else if(q->lastresumed)          /* Has been paused and resumed. */
-       sofar = q->uptopause + time(0) - q->lastresumed;
+       sofar = q->uptopause + xtime(0) - q->lastresumed;
       else                             /* Currently paused. */
        sofar = q->uptopause;
     } else                             /* Never been paused. */
-      sofar = time(0) - q->played;
+      sofar = xtime(0) - q->played;
     q->sofar = sofar;
   }
 }
 
 static void queue_read_error(const char *msg,
                             void *u) {
-  fatal(0, "error parsing queue %s: %s", (const char *)u, msg);
+  disorder_fatal(0, "error parsing queue %s: %s", (const char *)u, msg);
 }
 
 static void queue_do_read(struct queue_entry *head, const char *path) {
@@ -70,7 +73,7 @@ static void queue_do_read(struct queue_entry *head, const char *path) {
   if(!(fp = fopen(path, "r"))) {
     if(errno == ENOENT)
       return;                  /* no queue */
-    fatal(errno, "error opening %s", path);
+    disorder_fatal(errno, "error opening %s", path);
   }
   head->next = head->prev = head;
   while(!inputline(path, fp, &buffer, '\n')) {
@@ -102,10 +105,11 @@ static void queue_do_read(struct queue_entry *head, const char *path) {
     if(head == &qhead
        && (!q->track
           || !q->when))
-      fatal(0, "incomplete queue entry in %s", path);
+      disorder_fatal(0, "incomplete queue entry in %s", path);
     queue_insert_entry(head->prev, q);
   }
-  if(ferror(fp)) fatal(errno, "error reading %s", path);
+  if(ferror(fp))
+    disorder_fatal(errno, "error reading %s", path);
   fclose(fp);
 }
 
@@ -132,15 +136,15 @@ static void queue_do_write(const struct queue_entry *head, const char *path) {
   struct queue_entry *q;
 
   byte_xasprintf(&tmp, "%s.new", path);
-  if(!(fp = fopen(tmp, "w"))) fatal(errno, "error opening %s", tmp);
+  if(!(fp = fopen(tmp, "w"))) disorder_fatal(errno, "error opening %s", tmp);
   /* Save version indicator */
   if(fprintf(fp, "#1\n") < 0)
-    fatal(errno, "error writing %s", tmp);
+    disorder_fatal(errno, "error writing %s", tmp);
   for(q = head->next; q != head; q = q->next)
     if(fprintf(fp, "%s\n", queue_marshall(q)) < 0)
-      fatal(errno, "error writing %s", tmp);
-  if(fclose(fp) < 0) fatal(errno, "error closing %s", tmp);
-  if(rename(tmp, path) < 0) fatal(errno, "error replacing %s", path);
+      disorder_fatal(errno, "error writing %s", tmp);
+  if(fclose(fp) < 0) disorder_fatal(errno, "error closing %s", tmp);
+  if(rename(tmp, path) < 0) disorder_fatal(errno, "error replacing %s", path);
 }
 
 void queue_write(void) {
index 2874357..858edbc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004-2008 Richard Kettlewell
+ * Copyright (C) 2004-2009 Richard Kettlewell
  *
  * 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
  */
 
 #include "disorder-server.h"
+#include "basen.h"
 
 #ifndef NONCE_SIZE
 # define NONCE_SIZE 16
 #endif
 
 #ifndef CONFIRM_SIZE
-# define CONFIRM_SIZE 10
+/** @brief Size of nonce in confirmation string in 32-bit words
+ *
+ * 64 bits gives 11 digits (in base 62).
+ */
+# define CONFIRM_SIZE 2
 #endif
 
 int volume_left, volume_right;         /* last known volume */
@@ -39,6 +44,34 @@ struct listener {
   int pf;
 };
 
+struct conn;
+
+/** @brief Signature for line reader callback
+ * @param c Connection
+ * @param line Line
+ * @return 0 if incomplete, 1 if complete
+ *
+ * @p line is 0-terminated and excludes the newline.  It points into the
+ * input buffer so will become invalid shortly.
+ */
+typedef int line_reader_type(struct conn *c,
+                             char *line);
+
+/** @brief Signature for with-body command callbacks
+ * @param c Connection
+ * @param body List of body lines
+ * @param nbody Number of body lines
+ * @param u As passed to fetch_body()
+ * @return 0 to suspend input, 1 if complete
+ *
+ * The body strings are allocated (so survive indefinitely) and don't include
+ * newlines.
+ */
+typedef int body_callback_type(struct conn *c,
+                               char **body,
+                               int nbody,
+                               void *u);
+
 /** @brief One client connection */
 struct conn {
   /** @brief Read commands from here */
@@ -72,6 +105,18 @@ struct conn {
   struct conn *next;
   /** @brief True if pending rescan had 'wait' set */
   int rescan_wait;
+  /** @brief Playlist that this connection locks */
+  const char *locked_playlist;
+  /** @brief When that playlist was locked */
+  time_t locked_when;
+  /** @brief Line reader function */
+  line_reader_type *line_reader;
+  /** @brief Called when command body has been read */
+  body_callback_type *body_callback;
+  /** @brief Passed to @c body_callback */
+  void *body_u;
+  /** @brief Accumulating body */
+  struct vector body[1];
 };
 
 /** @brief Linked list of connections */
@@ -83,6 +128,15 @@ static int reader_callback(ev_source *ev,
                           size_t bytes,
                           int eof,
                           void *u);
+static int c_playlist_set_body(struct conn *c,
+                               char **body,
+                               int nbody,
+                               void *u);
+static int fetch_body(struct conn *c,
+                      body_callback_type body_callback,
+                      void *u);
+static int body_line(struct conn *c, char *line);
+static int command(struct conn *c, char *line);
 
 static const char *noyes[] = { "no", "yes" };
 
@@ -111,7 +165,7 @@ static int writer_error(ev_source attribute((unused)) *ev,
     D(("S%x writer completed", c->tag));
   } else {
     if(errno_value != EPIPE)
-      error(errno_value, "S%x write error on socket", c->tag);
+      disorder_error(errno_value, "S%x write error on socket", c->tag);
     if(c->r) {
       D(("cancel reader"));
       ev_reader_cancel(c->r);
@@ -135,7 +189,7 @@ static int reader_error(ev_source attribute((unused)) *ev,
   struct conn *c = u;
 
   D(("server reader_error S%x %d", c->tag, errno_value));
-  error(errno_value, "S%x read error on socket", c->tag);
+  disorder_error(errno_value, "S%x read error on socket", c->tag);
   if(c->w)
     ev_writer_close(c->w);
   c->w = 0;
@@ -188,14 +242,14 @@ static int c_play(struct conn *c, char **vec,
     sink_writes(ev_writer_sink(c->w), "550 cannot resolve track\n");
     return 1;
   }
-  q = queue_add(track, c->who, WHERE_BEFORE_RANDOM, origin_picked);
+  q = queue_add(track, c->who, WHERE_BEFORE_RANDOM, NULL, origin_picked);
   queue_write();
-  /* If we added the first track, and something is playing, then prepare the
-   * new track.  If nothing is playing then we don't bother as it wouldn't gain
-   * anything. */
-  if(q == qhead.next && playing)
-    prepare(c->ev, q);
   sink_printf(ev_writer_sink(c->w), "252 %s\n", q->id);
+  /* We make sure the track at the head of the queue is prepared, just in case
+   * we added it.  We could be more subtle but prepare() will ensure we don't
+   * prepare the same track twice so there's no point. */
+  if(qhead.next != &qhead)
+    prepare(c->ev, qhead.next);
   /* If the queue was empty but we are for some reason paused then
    * unpause. */
   if(!playing) resume_playing(0);
@@ -203,6 +257,46 @@ static int c_play(struct conn *c, char **vec,
   return 1;                    /* completed */
 }
 
+static int c_playafter(struct conn *c, char **vec,
+                 int attribute((unused)) nvec) {
+  const char *track;
+  struct queue_entry *q;
+  const char *afterme = vec[0];
+
+  for(int n = 1; n < nvec; ++n) {
+    if(!trackdb_exists(vec[n])) {
+      sink_writes(ev_writer_sink(c->w), "550 track is not in database\n");
+      return 1;
+    }
+    if(!(track = trackdb_resolve(vec[n]))) {
+      sink_writes(ev_writer_sink(c->w), "550 cannot resolve track\n");
+      return 1;
+    }
+    q = queue_add(track, c->who, WHERE_AFTER, afterme, origin_picked);
+    if(!q) {
+      sink_printf(ev_writer_sink(c->w), "550 No such ID\n");
+      return 1;
+    }
+    disorder_info("added %s as %s after %s", track, q->id, afterme);
+    afterme = q->id;
+  }
+  queue_write();
+  sink_printf(ev_writer_sink(c->w), "252 OK\n");
+  /* We make sure the track at the head of the queue is prepared, just in case
+   * we added it.  We could be more subtle but prepare() will ensure we don't
+   * prepare the same track twice so there's no point. */
+  if(qhead.next != &qhead) {
+    prepare(c->ev, qhead.next);
+    disorder_info("prepared %s", qhead.next->id);
+  }
+  /* If the queue was empty but we are for some reason paused then
+   * unpause. */
+  if(!playing)
+    resume_playing(0);
+  play(c->ev);
+  return 1;                    /* completed */
+}
+
 static int c_remove(struct conn *c, char **vec,
                    int attribute((unused)) nvec) {
   struct queue_entry *q;
@@ -212,7 +306,7 @@ static int c_remove(struct conn *c, char **vec,
     return 1;
   }
   if(!right_removable(c->rights, c->who, q)) {
-    error(0, "%s attempted remove but lacks required rights", c->who);
+    disorder_error(0, "%s attempted remove but lacks required rights", c->who);
     sink_writes(ev_writer_sink(c->w),
                "510 Not authorized to remove that track\n");
     return 1;
@@ -241,7 +335,7 @@ static int c_scratch(struct conn *c,
    * playing track then you will get 550 if you weren't authorized to scratch
    * the currently playing track. */
   if(!right_scratchable(c->rights, c->who, playing)) {
-    error(0, "%s attempted scratch but lacks required rights", c->who);
+    disorder_error(0, "%s attempted scratch but lacks required rights", c->who);
     sink_writes(ev_writer_sink(c->w),
                "510 Not authorized to scratch that track\n");
     return 1;
@@ -286,7 +380,7 @@ static int c_resume(struct conn *c,
 static int c_shutdown(struct conn *c,
                      char attribute((unused)) **vec,
                      int attribute((unused)) nvec) {
-  info("S%x shut down by %s", c->tag, c->who);
+  disorder_info("S%x shut down by %s", c->tag, c->who);
   sink_writes(ev_writer_sink(c->w), "250 shutting down\n");
   ev_writer_flush(c->w);
   quit(c->ev);
@@ -295,7 +389,7 @@ static int c_shutdown(struct conn *c,
 static int c_reconfigure(struct conn *c,
                         char attribute((unused)) **vec,
                         int attribute((unused)) nvec) {
-  info("S%x reconfigure by %s", c->tag, c->who);
+  disorder_info("S%x reconfigure by %s", c->tag, c->who);
   if(reconfigure(c->ev, 1))
     sink_writes(ev_writer_sink(c->w), "550 error reading new config\n");
   else
@@ -363,9 +457,9 @@ static int c_rescan(struct conn *c,
     }
   }
   /* Report what was requested */
-  info("S%x rescan by %s (%s %s)", c->tag, c->who,
-       flag_wait ? "wait" : "",
-       flag_fresh ? "fresh" : "");
+  disorder_info("S%x rescan by %s (%s %s)", c->tag, c->who,
+               flag_wait ? "wait" : "",
+               flag_fresh ? "fresh" : "");
   if(trackdb_rescan_underway()) {
     if(flag_fresh) {
       /* We want a fresh rescan but there is already one underway.  Arrange a
@@ -439,13 +533,14 @@ static const char *connection_host(struct conn *c) {
   /* get connection data */
   l = sizeof u;
   if(getpeername(c->fd, &u.sa, &l) < 0) {
-    error(errno, "S%x error calling getpeername", c->tag);
+    disorder_error(errno, "S%x error calling getpeername", c->tag);
     return 0;
   }
   if(c->l->pf != PF_UNIX) {
     if((n = getnameinfo(&u.sa, l,
                        host, sizeof host, 0, 0, NI_NUMERICHOST))) {
-      error(0, "S%x error calling getnameinfo: %s", c->tag, gai_strerror(n));
+      disorder_error(0, "S%x error calling getnameinfo: %s",
+                    c->tag, gai_strerror(n));
       return 0;
     }
     return xstrdup(host);
@@ -473,20 +568,21 @@ static int c_user(struct conn *c,
   k = trackdb_getuserinfo(vec[0]);
   /* reject nonexistent users */
   if(!k) {
-    error(0, "S%x unknown user '%s' from %s", c->tag, vec[0], host);
+    disorder_error(0, "S%x unknown user '%s' from %s", c->tag, vec[0], host);
     sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
     return 1;
   }
   /* reject unconfirmed users */
   if(kvp_get(k, "confirmation")) {
-    error(0, "S%x unconfirmed user '%s' from %s", c->tag, vec[0], host);
+    disorder_error(0, "S%x unconfirmed user '%s' from %s",
+                  c->tag, vec[0], host);
     sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
     return 1;
   }
   password = kvp_get(k, "password");
   if(!password) password = "";
   if(parse_rights(kvp_get(k, "rights"), &rights, 1)) {
-    error(0, "error parsing rights for %s", vec[0]);
+    disorder_error(0, "error parsing rights for %s", vec[0]);
     sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
     return 1;
   }
@@ -498,14 +594,15 @@ static int c_user(struct conn *c,
     c->rights = rights;
     /* currently we only bother logging remote connections */
     if(strcmp(host, "local"))
-      info("S%x %s connected from %s", c->tag, vec[0], host);
+      disorder_info("S%x %s connected from %s", c->tag, vec[0], host);
     else
       c->rights |= RIGHT__LOCAL;
     sink_writes(ev_writer_sink(c->w), "230 OK\n");
     return 1;
   }
   /* oops, response was wrong */
-  info("S%x authentication failure for %s from %s", c->tag, vec[0], host);
+  disorder_info("S%x authentication failure for %s from %s",
+               c->tag, vec[0], host);
   sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
   return 1;
 }
@@ -536,13 +633,13 @@ static int c_queue(struct conn *c,
       queue_fix_sofar(playing);
       if((l = trackdb_get(playing->track, "_length"))
         && (length = atol(l))) {
-       time(&when);
+       xtime(&when);
        when += length - playing->sofar + config->gap;
       }
     } else
       /* Nothing is playing but playing is enabled, so whatever is
        * first in the queue can be expected to start immediately. */
-      time(&when);
+      xtime(&when);
   }
   for(q = qhead.next; q != &qhead; q = q->next) {
     /* fill in estimated start time */
@@ -821,21 +918,23 @@ static int c_volume(struct conn *c,
   }
   rights = set ? RIGHT_VOLUME : RIGHT_READ;
   if(!(c->rights & rights)) {
-    error(0, "%s attempted to set volume but lacks required rights", c->who);
+    disorder_error(0, "%s attempted to set volume but lacks required rights",
+                  c->who);
     sink_writes(ev_writer_sink(c->w), "510 Prohibited\n");
     return 1;
   }
-  if(mixer_control(-1/*as configured*/, &l, &r, set))
+  if(!api || !api->set_volume) {
     sink_writes(ev_writer_sink(c->w), "550 error accessing mixer\n");
-  else {
-    sink_printf(ev_writer_sink(c->w), "252 %d %d\n", l, r);
-    if(l != volume_left || r != volume_right) {
-      volume_left = l;
-      volume_right = r;
-      snprintf(lb, sizeof lb, "%d", l);
-      snprintf(rb, sizeof rb, "%d", r);
-      eventlog("volume", lb, rb, (char *)0);
-    }
+    return 1;
+  }
+  (set ? api->set_volume : api->get_volume)(&l, &r);
+  sink_printf(ev_writer_sink(c->w), "252 %d %d\n", l, r);
+  if(l != volume_left || r != volume_right) {
+    volume_left = l;
+    volume_right = r;
+    snprintf(lb, sizeof lb, "%d", l);
+    snprintf(rb, sizeof rb, "%d", r);
+    eventlog("volume", lb, rb, (char *)0);
   }
   return 1;
 }
@@ -888,7 +987,7 @@ static void logclient(const char *msg, void *user) {
       return;
   }
   sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" %s\n",
-             (uintmax_t)time(0), msg);
+             (uintmax_t)xtime(0), msg);
 }
 
 static int c_log(struct conn *c,
@@ -898,7 +997,7 @@ static int c_log(struct conn *c,
 
   sink_writes(ev_writer_sink(c->w), "254 OK\n");
   /* pump out initial state */
-  time(&now);
+  xtime(&now);
   sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" state %s\n",
              (uintmax_t)now, 
              playing_is_enabled() ? "enable_play" : "disable_play");
@@ -949,7 +1048,7 @@ static int c_move(struct conn *c,
     return 1;
   }
   if(!has_move_rights(c, &q, 1)) {
-    error(0, "%s attempted move but lacks required rights", c->who);
+    disorder_error(0, "%s attempted move but lacks required rights", c->who);
     sink_writes(ev_writer_sink(c->w),
                "510 Not authorized to move that track\n");
     return 1;
@@ -984,7 +1083,8 @@ static int c_moveafter(struct conn *c,
       return 1;
     }
   if(!has_move_rights(c, qs, nvec)) {
-    error(0, "%s attempted moveafter but lacks required rights", c->who);
+    disorder_error(0, "%s attempted moveafter but lacks required rights",
+                  c->who);
     sink_writes(ev_writer_sink(c->w),
                "510 Not authorized to move those tracks\n");
     return 1;
@@ -1024,21 +1124,25 @@ static int c_resolve(struct conn *c,
   return 1;
 }
 
-static int c_tags(struct conn *c,
-                 char attribute((unused)) **vec,
-                 int attribute((unused)) nvec) {
-  char **tags = trackdb_alltags();
-  
-  sink_printf(ev_writer_sink(c->w), "253 Tag list follows\n");
-  while(*tags) {
+static int list_response(struct conn *c,
+                         const char *reply,
+                         char **list) {
+  sink_printf(ev_writer_sink(c->w), "253 %s\n", reply);
+  while(*list) {
     sink_printf(ev_writer_sink(c->w), "%s%s\n",
-               **tags == '.' ? "." : "", *tags);
-    ++tags;
+               **list == '.' ? "." : "", *list);
+    ++list;
   }
   sink_writes(ev_writer_sink(c->w), ".\n");
   return 1;                            /* completed */
 }
 
+static int c_tags(struct conn *c,
+                 char attribute((unused)) **vec,
+                 int attribute((unused)) nvec) {
+  return list_response(c, "Tag list follows", trackdb_alltags());
+}
+
 static int c_set_global(struct conn *c,
                        char **vec,
                        int attribute((unused)) nvec) {
@@ -1098,10 +1202,13 @@ static int c_new(struct conn *c,
 static int c_rtp_address(struct conn *c,
                         char attribute((unused)) **vec,
                         int attribute((unused)) nvec) {
-  if(config->api == BACKEND_NETWORK) {
+  if(api == &uaudio_rtp) {
+    char **addr;
+
+    netaddress_format(&config->broadcast, NULL, &addr);
     sink_printf(ev_writer_sink(c->w), "252 %s %s\n",
-               quoteutf8(config->broadcast.s[0]),
-               quoteutf8(config->broadcast.s[1]));
+               quoteutf8(addr[1]),
+               quoteutf8(addr[2]));
   } else
     sink_writes(ev_writer_sink(c->w), "550 No RTP\n");
   return 1;
@@ -1135,7 +1242,7 @@ static int c_cookie(struct conn *c,
   c->cookie = vec[0];
   c->rights = rights;
   if(strcmp(host, "local"))
-    info("S%x %s connected with cookie from %s", c->tag, user, host);
+    disorder_info("S%x %s connected with cookie from %s", c->tag, user, host);
   else
     c->rights |= RIGHT__LOCAL;
   /* Response contains username so client knows who they are acting as */
@@ -1172,7 +1279,7 @@ static int c_adduser(struct conn *c,
   const char *rights;
 
   if(!config->remote_userman && !(c->rights & RIGHT__LOCAL)) {
-    error(0, "S%x: remote adduser", c->tag);
+    disorder_error(0, "S%x: remote adduser", c->tag);
     sink_writes(ev_writer_sink(c->w), "550 Remote user management is disabled\n");
     return 1;
   }
@@ -1198,7 +1305,7 @@ static int c_deluser(struct conn *c,
   struct conn *d;
 
   if(!config->remote_userman && !(c->rights & RIGHT__LOCAL)) {
-    error(0, "S%x: remote deluser", c->tag);
+    disorder_error(0, "S%x: remote deluser", c->tag);
     sink_writes(ev_writer_sink(c->w), "550 Remote user management is disabled\n");
     return 1;
   }
@@ -1220,7 +1327,7 @@ static int c_edituser(struct conn *c,
   struct conn *d;
 
   if(!config->remote_userman && !(c->rights & RIGHT__LOCAL)) {
-    error(0, "S%x: remote edituser", c->tag);
+    disorder_error(0, "S%x: remote edituser", c->tag);
     sink_writes(ev_writer_sink(c->w), "550 Remote user management is disabled\n");
     return 1;
   }
@@ -1253,7 +1360,7 @@ static int c_edituser(struct conn *c,
             if(d->lo)
               sink_printf(ev_writer_sink(d->w),
                           "%"PRIxMAX" rights_changed %s\n",
-                          (uintmax_t)time(0),
+                          (uintmax_t)xtime(0),
                           quoteutf8(new_rights));
           }
         }
@@ -1261,7 +1368,8 @@ static int c_edituser(struct conn *c,
     }
     sink_writes(ev_writer_sink(c->w), "250 OK\n");
   } else {
-    error(0, "%s attempted edituser but lacks required rights", c->who);
+    disorder_error(0, "%s attempted edituser but lacks required rights",
+                  c->who);
     sink_writes(ev_writer_sink(c->w), "510 Restricted to administrators\n");
   }
   return 1;
@@ -1278,7 +1386,7 @@ static int c_userinfo(struct conn *c,
   if(!config->remote_userman
      && !(c->rights & RIGHT__LOCAL)
      && strcmp(vec[1], "rights")) {
-    error(0, "S%x: remote userinfo %s %s", c->tag, vec[0], vec[1]);
+    disorder_error(0, "S%x: remote userinfo %s %s", c->tag, vec[0], vec[1]);
     sink_writes(ev_writer_sink(c->w), "550 Remote user management is disabled\n");
     return 1;
   }
@@ -1296,7 +1404,8 @@ static int c_userinfo(struct conn *c,
     else
       sink_writes(ev_writer_sink(c->w), "550 No such user\n");
   } else {
-    error(0, "%s attempted userinfo but lacks required rights", c->who);
+    disorder_error(0, "%s attempted userinfo but lacks required rights",
+                  c->who);
     sink_writes(ev_writer_sink(c->w), "510 Restricted to administrators\n");
   }
   return 1;
@@ -1305,43 +1414,26 @@ static int c_userinfo(struct conn *c,
 static int c_users(struct conn *c,
                   char attribute((unused)) **vec,
                   int attribute((unused)) nvec) {
-  /* TODO de-dupe with c_tags */
-  char **users = trackdb_listusers();
-
-  sink_writes(ev_writer_sink(c->w), "253 User list follows\n");
-  while(*users) {
-    sink_printf(ev_writer_sink(c->w), "%s%s\n",
-               **users == '.' ? "." : "", *users);
-    ++users;
-  }
-  sink_writes(ev_writer_sink(c->w), ".\n");
-  return 1;                            /* completed */
+  return list_response(c, "User list follows", trackdb_listusers());
 }
 
-/** @brief Base64 mapping table for confirmation strings
- *
- * This is used with generic_to_base64() and generic_base64().  We cannot use
- * the MIME table as that contains '+' and '=' which get quoted when
- * URL-encoding.  (The CGI still does the URL encoding but it is desirable to
- * avoid it being necessary.)
- */
-static const char confirm_base64_table[] =
-  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/.*";
-
 static int c_register(struct conn *c,
                      char **vec,
                      int attribute((unused)) nvec) {
-  char *buf, *cs;
-  size_t bufsize;
-  int offset;
-
-  /* The confirmation string is base64(username;nonce) */
-  bufsize = strlen(vec[0]) + CONFIRM_SIZE + 2;
-  buf = xmalloc_noptr(bufsize);
-  offset = byte_snprintf(buf, bufsize, "%s;", vec[0]);
-  gcry_randomize(buf + offset, CONFIRM_SIZE, GCRY_STRONG_RANDOM);
-  cs = generic_to_base64((uint8_t *)buf, offset + CONFIRM_SIZE,
-                        confirm_base64_table);
+  char *cs;
+  uint32_t nonce[CONFIRM_SIZE];
+  char nonce_str[(32 * CONFIRM_SIZE) / 5 + 1];
+
+  /* The confirmation string is username/base62(nonce).  The confirmation
+   * process will pick the username back out to identify them but the _whole_
+   * string is used as the confirmation string.  Base 62 means we used only
+   * letters and digits, minimizing the chance of the URL being mispasted. */
+  gcry_randomize(nonce, sizeof nonce, GCRY_STRONG_RANDOM);
+  if(basen(nonce, CONFIRM_SIZE, nonce_str, sizeof nonce_str, 62)) {
+    disorder_error(0, "buffer too small encoding confirmation string");
+    sink_writes(ev_writer_sink(c->w), "550 Cannot create user\n");
+  }
+  byte_xasprintf(&cs, "%s/%s", vec[0], nonce_str);
   if(trackdb_adduser(vec[0], vec[1], config->default_rights, vec[2], cs))
     sink_writes(ev_writer_sink(c->w), "550 Cannot create user\n");
   else
@@ -1352,7 +1444,6 @@ static int c_register(struct conn *c,
 static int c_confirm(struct conn *c,
                     char **vec,
                     int attribute((unused)) nvec) {
-  size_t nuser;
   char *user, *sep;
   rights_type rights;
   const char *host;
@@ -1362,12 +1453,12 @@ static int c_confirm(struct conn *c,
     sink_writes(ev_writer_sink(c->w), "530 Authentication failure\n");
     return 1;
   }
-  if(!(user = generic_base64(vec[0], &nuser, confirm_base64_table))
-     || !(sep = memchr(user, ';', nuser))) {
+  /* Picking the LAST / means we don't (here) rule out slashes in usernames. */
+  if(!(sep = strrchr(vec[0], '/'))) {
     sink_writes(ev_writer_sink(c->w), "550 Malformed confirmation string\n");
     return 1;
   }
-  *sep = 0;
+  user = xstrndup(vec[0], sep - vec[0]);
   if(trackdb_confirm(user, vec[0], &rights))
     sink_writes(ev_writer_sink(c->w), "550 Incorrect confirmation string\n");
   else {
@@ -1375,7 +1466,7 @@ static int c_confirm(struct conn *c,
     c->cookie = 0;
     c->rights = rights;
     if(strcmp(host, "local"))
-      info("S%x %s confirmed from %s", c->tag, user, host);
+      disorder_info("S%x %s confirmed from %s", c->tag, user, host);
     else
       c->rights |= RIGHT__LOCAL;
     /* Response contains username so client knows who they are acting as */
@@ -1395,7 +1486,7 @@ static int sent_reminder(ev_source attribute((unused)) *ev,
   if(!status) {
     sink_writes(ev_writer_sink(c->w), "250 OK\n");
   } else {
-    error(0, "reminder subprocess %s", wstat(status));
+    disorder_error(0, "reminder subprocess %s", wstat(status));
     sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
   }
   /* Re-enable this connection */
@@ -1415,24 +1506,24 @@ static int c_reminder(struct conn *c,
   static hash *last_reminder;
 
   if(!config->mail_sender) {
-    error(0, "cannot send password reminders because mail_sender not set");
+    disorder_error(0, "cannot send password reminders because mail_sender not set");
     sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
     return 1;
   }
   if(!(k = trackdb_getuserinfo(vec[0]))) {
-    error(0, "reminder for user '%s' who does not exist", vec[0]);
+    disorder_error(0, "reminder for user '%s' who does not exist", vec[0]);
     sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
     return 1;
   }
   if(!(email = kvp_get(k, "email"))
      || !email_valid(email)) {
-    error(0, "user '%s' has no valid email address", vec[0]);
+    disorder_error(0, "user '%s' has no valid email address", vec[0]);
     sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
     return 1;
   }
   if(!(password = kvp_get(k, "password"))
      || !*password) {
-    error(0, "user '%s' has no password", vec[0]);
+    disorder_error(0, "user '%s' has no password", vec[0]);
     sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
     return 1;
   }
@@ -1442,9 +1533,9 @@ static int c_reminder(struct conn *c,
   if(!last_reminder)
     last_reminder = hash_new(sizeof (time_t));
   last = hash_find(last_reminder, vec[0]);
-  time(&now);
+  xtime(&now);
   if(last && now < *last + config->reminder_interval) {
-    error(0, "sent a password reminder to '%s' too recently", vec[0]);
+    disorder_error(0, "sent a password reminder to '%s' too recently", vec[0]);
     sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
     return 1;
   }
@@ -1457,7 +1548,7 @@ static int c_reminder(struct conn *c,
 "\n"
 "  %s\n", password);
   if(!(text = mime_encode_text(text, &charset, &encoding)))
-    fatal(0, "cannot encode email");
+    disorder_fatal(0, "cannot encode email");
   byte_xasprintf((char **)&content_type, "text/plain;charset=%s",
                 quote822(charset, 0));
   pid = sendmail_subprocess("", config->mail_sender, email,
@@ -1468,7 +1559,7 @@ static int c_reminder(struct conn *c,
     return 1;
   }
   hash_add(last_reminder, vec[0], &now, HASH_INSERT_OR_REPLACE);
-  info("sending a passsword reminder to user '%s'", vec[0]);
+  disorder_info("sending a passsword reminder to user '%s'", vec[0]);
   /* We can only continue when the subprocess finishes */
   ev_child(c->ev, pid, 0, sent_reminder, c);
   return 0;
@@ -1598,6 +1689,152 @@ static int c_adopt(struct conn *c,
   return 1;
 }
 
+static int playlist_response(struct conn *c,
+                             int err) {
+  switch(err) {
+  case 0:
+    assert(!"cannot cope with success");
+  case EACCES:
+    sink_writes(ev_writer_sink(c->w), "550 Access denied\n");
+    break;
+  case EINVAL:
+    sink_writes(ev_writer_sink(c->w), "550 Invalid playlist name\n");
+    break;
+  case ENOENT:
+    sink_writes(ev_writer_sink(c->w), "555 No such playlist\n");
+    break;
+  default:
+    sink_writes(ev_writer_sink(c->w), "550 Error accessing playlist\n");
+    break;
+  }
+  return 1;
+}
+
+static int c_playlist_get(struct conn *c,
+                         char **vec,
+                         int attribute((unused)) nvec) {
+  char **tracks;
+  int err;
+
+  if(!(err = trackdb_playlist_get(vec[0], c->who, &tracks, 0, 0)))
+    return list_response(c, "Playlist contents follows", tracks);
+  else
+    return playlist_response(c, err);
+}
+
+static int c_playlist_set(struct conn *c,
+                         char **vec,
+                         int attribute((unused)) nvec) {
+  return fetch_body(c, c_playlist_set_body, vec[0]);
+}
+
+static int c_playlist_set_body(struct conn *c,
+                               char **body,
+                               int nbody,
+                               void *u) {
+  const char *playlist = u;
+  int err;
+
+  if(!c->locked_playlist
+     || strcmp(playlist, c->locked_playlist)) {
+    sink_writes(ev_writer_sink(c->w), "550 Playlist is not locked\n");
+    return 1;
+  }
+  if(!(err = trackdb_playlist_set(playlist, c->who,
+                                  body, nbody, 0))) {
+    sink_printf(ev_writer_sink(c->w), "250 OK\n");
+    return 1;
+  } else
+    return playlist_response(c, err);
+}
+
+static int c_playlist_get_share(struct conn *c,
+                                char **vec,
+                                int attribute((unused)) nvec) {
+  char *share;
+  int err;
+
+  if(!(err = trackdb_playlist_get(vec[0], c->who, 0, 0, &share))) {
+    sink_printf(ev_writer_sink(c->w), "252 %s\n", quoteutf8(share));
+    return 1;
+  } else
+    return playlist_response(c, err);
+}
+
+static int c_playlist_set_share(struct conn *c,
+                                char **vec,
+                                int attribute((unused)) nvec) {
+  int err;
+
+  if(!(err = trackdb_playlist_set(vec[0], c->who, 0, 0, vec[1]))) {
+    sink_printf(ev_writer_sink(c->w), "250 OK\n");
+    return 1;
+  } else
+    return playlist_response(c, err);
+}
+
+static int c_playlists(struct conn *c,
+                       char attribute((unused)) **vec,
+                       int attribute((unused)) nvec) {
+  char **p;
+
+  trackdb_playlist_list(c->who, &p, 0);
+  return list_response(c, "List of playlists follows", p);
+}
+
+static int c_playlist_delete(struct conn *c,
+                             char **vec,
+                             int attribute((unused)) nvec) {
+  int err;
+  
+  if(!(err = trackdb_playlist_delete(vec[0], c->who))) {
+    sink_writes(ev_writer_sink(c->w), "250 OK\n");
+    return 1;
+  } else
+    return playlist_response(c, err);
+}
+
+static int c_playlist_lock(struct conn *c,
+                           char **vec,
+                           int attribute((unused)) nvec) {
+  int err;
+  struct conn *cc;
+
+  /* Check we're allowed to modify this playlist */
+  if((err = trackdb_playlist_set(vec[0], c->who, 0, 0, 0)))
+    return playlist_response(c, err);
+  /* If we hold a lock don't allow a new one */
+  if(c->locked_playlist) {
+    sink_writes(ev_writer_sink(c->w), "550 Already holding a lock\n");
+    return 1;
+  }
+  /* See if some other connection locks the same playlist */
+  for(cc = connections; cc; cc = cc->next)
+    if(cc->locked_playlist && !strcmp(cc->locked_playlist, vec[0]))
+      break;
+  if(cc) {
+    /* TODO: implement config->playlist_lock_timeout */
+    sink_writes(ev_writer_sink(c->w), "550 Already locked\n");
+    return 1;
+  }
+  c->locked_playlist = xstrdup(vec[0]);
+  time(&c->locked_when);
+  sink_writes(ev_writer_sink(c->w), "250 Acquired lock\n");
+  return 1;
+}
+
+static int c_playlist_unlock(struct conn *c,
+                             char attribute((unused)) **vec,
+                             int attribute((unused)) nvec) {
+  if(!c->locked_playlist) {
+    sink_writes(ev_writer_sink(c->w), "550 Not holding a lock\n");
+    return 1;
+  }
+  c->locked_playlist = 0;
+  sink_writes(ev_writer_sink(c->w), "250 Released lock\n");
+  return 1;
+}
+
 static const struct command {
   /** @brief Command name */
   const char *name;
@@ -1643,7 +1880,16 @@ static const struct command {
   { "part",           3, 3,       c_part,           RIGHT_READ },
   { "pause",          0, 0,       c_pause,          RIGHT_PAUSE },
   { "play",           1, 1,       c_play,           RIGHT_PLAY },
+  { "playafter",      2, INT_MAX, c_playafter,      RIGHT_PLAY },
   { "playing",        0, 0,       c_playing,        RIGHT_READ },
+  { "playlist-delete",    1, 1,   c_playlist_delete,    RIGHT_PLAY },
+  { "playlist-get",       1, 1,   c_playlist_get,       RIGHT_READ },
+  { "playlist-get-share", 1, 1,   c_playlist_get_share, RIGHT_READ },
+  { "playlist-lock",      1, 1,   c_playlist_lock,      RIGHT_PLAY },
+  { "playlist-set",       1, 1,   c_playlist_set,       RIGHT_PLAY },
+  { "playlist-set-share", 2, 2,   c_playlist_set_share, RIGHT_PLAY },
+  { "playlist-unlock",    0, 0,   c_playlist_unlock,    RIGHT_PLAY },
+  { "playlists",          0, 0,   c_playlists,          RIGHT_READ },
   { "prefs",          1, 1,       c_prefs,          RIGHT_READ },
   { "queue",          0, 0,       c_queue,          RIGHT_READ },
   { "random-disable", 0, 0,       c_random_disable, RIGHT_GLOBAL_PREFS },
@@ -1679,13 +1925,58 @@ static const struct command {
   { "volume",         0, 2,       c_volume,         RIGHT_READ|RIGHT_VOLUME }
 };
 
+/** @brief Fetch a command body
+ * @param c Connection
+ * @param body_callback Called with body
+ * @param u Passed to body_callback
+ * @return 1
+ */
+static int fetch_body(struct conn *c,
+                      body_callback_type body_callback,
+                      void *u) {
+  assert(c->line_reader == command);
+  c->line_reader = body_line;
+  c->body_callback = body_callback;
+  c->body_u = u;
+  vector_init(c->body);
+  return 1;
+}
+
+/** @brief @ref line_reader_type callback for command body lines
+ * @param c Connection
+ * @param line Line
+ * @return 1 if complete, 0 if incomplete
+ *
+ * Called from reader_callback().
+ */
+static int body_line(struct conn *c,
+                     char *line) {
+  if(*line == '.') {
+    ++line;
+    if(!*line) {
+      /* That's the lot */
+      c->line_reader = command;
+      vector_terminate(c->body);
+      return c->body_callback(c, c->body->vec, c->body->nvec, c->body_u);
+    }
+  }
+  vector_append(c->body, xstrdup(line));
+  return 1;                             /* completed */
+}
+
 static void command_error(const char *msg, void *u) {
   struct conn *c = u;
 
   sink_printf(ev_writer_sink(c->w), "500 parse error: %s\n", msg);
 }
 
-/* process a command.  Return 1 if complete, 0 if incomplete. */
+/** @brief @ref line_reader_type callback for commands
+ * @param c Connection
+ * @param line Line
+ * @return 1 if complete, 0 if incomplete
+ *
+ * Called from reader_callback().
+ */
 static int command(struct conn *c, char *line) {
   char **vec;
   int nvec, n;
@@ -1709,7 +2000,8 @@ static int command(struct conn *c, char *line) {
   else {
     if(commands[n].rights
        && !(c->rights & commands[n].rights)) {
-      error(0, "%s attempted %s but lacks required rights", c->who ? c->who : "NULL",
+      disorder_error(0, "%s attempted %s but lacks required rights",
+                    c->who ? c->who : "NULL",
            commands[n].name);
       sink_writes(ev_writer_sink(c->w), "510 Prohibited\n");
       return 1;
@@ -1756,7 +2048,7 @@ static int reader_callback(ev_source attribute((unused)) *ev,
   while((eol = memchr(ptr, '\n', bytes))) {
     *eol++ = 0;
     ev_reader_consume(reader, eol - (char *)ptr);
-    complete = command(c, ptr);
+    complete = c->line_reader(c, ptr);  /* usually command() */
     bytes -= (eol - (char *)ptr);
     ptr = eol;
     if(!complete) {
@@ -1772,7 +2064,7 @@ static int reader_callback(ev_source attribute((unused)) *ev,
   }
   if(eof) {
     if(bytes)
-      error(0, "S%x unterminated line", c->tag);
+      disorder_error(0, "S%x unterminated line", c->tag);
     D(("normal reader close"));
     c->r = 0;
     if(c->w) {
@@ -1803,7 +2095,7 @@ static int listen_callback(ev_source *ev,
   c->w = ev_writer_new(ev, fd, writer_error, c,
                       "client writer");
   if(!c->w) {
-    error(0, "ev_writer_new for file inbound connection (fd=%d) failed",
+    disorder_error(0, "ev_writer_new for file inbound connection (fd=%d) failed",
           fd);
     close(fd);
     return 0;
@@ -1813,12 +2105,15 @@ static int listen_callback(ev_source *ev,
   if(!c->r)
     /* Main reason for failure is the FD is too big and that will already have
      * been handled */
-    fatal(0, "ev_reader_new for file inbound connection (fd=%d) failed", fd);
+    disorder_fatal(0,
+                  "ev_reader_new for file inbound connection (fd=%d) failed",
+                  fd);
   ev_tie(c->r, c->w);
   c->fd = fd;
   c->reader = reader_callback;
   c->l = l;
   c->rights = 0;
+  c->line_reader = command;
   connections = c;
   gcry_randomize(c->nonce, sizeof c->nonce, GCRY_STRONG_RANDOM);
   sink_printf(ev_writer_sink(c->w), "231 %d %s %s\n",
@@ -1839,7 +2134,7 @@ int server_start(ev_source *ev, int pf,
   fd = xsocket(pf, SOCK_STREAM, 0);
   xsetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
   if(bind(fd, sa, socklen) < 0) {
-    error(errno, "error binding to %s", name);
+    disorder_error(errno, "error binding to %s", name);
     return -1;
   }
   xlisten(fd, 128);
@@ -1849,6 +2144,7 @@ int server_start(ev_source *ev, int pf,
   l->pf = pf;
   if(ev_listen(ev, fd, listen_callback, l, "server listener"))
     exit(EXIT_FAILURE);
+  disorder_info("listening on %s", name);
   return fd;
 }
 
diff --git a/server/speaker-alsa.c b/server/speaker-alsa.c
deleted file mode 100644 (file)
index d10ec19..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * This file is part of DisOrder
- * Copyright (C) 2005, 2006, 2007 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file server/speaker-alsa.c
- * @brief Support for @ref BACKEND_ALSA */
-
-#include "common.h"
-
-#if HAVE_ALSA_ASOUNDLIB_H
-
-#include <unistd.h>
-#include <poll.h>
-#include <alsa/asoundlib.h>
-
-#include "configuration.h"
-#include "syscalls.h"
-#include "log.h"
-#include "speaker-protocol.h"
-#include "speaker.h"
-
-/** @brief The current PCM handle */
-static snd_pcm_t *pcm;
-
-/** @brief Last seen buffer size */
-static snd_pcm_uframes_t last_pcm_bufsize;
-
-/** @brief ALSA backend initialization */
-static void alsa_init(void) {
-  info("selected ALSA backend");
-}
-
-/** @brief Log ALSA parameters */
-static void log_params(snd_pcm_hw_params_t *hwparams,
-                       snd_pcm_sw_params_t *swparams) {
-  snd_pcm_uframes_t f;
-
-  return;                               /* too verbose */
-  if(hwparams) {
-    /* TODO */
-  }
-  if(swparams) {
-    snd_pcm_sw_params_get_silence_size(swparams, &f);
-    info("sw silence_size=%lu", (unsigned long)f);
-    snd_pcm_sw_params_get_silence_threshold(swparams, &f);
-    info("sw silence_threshold=%lu", (unsigned long)f);
-#if HAVE_SND_PCM_SW_PARAMS_GET_SLEEP_MIN
-    {
-      unsigned u;
-
-      snd_pcm_sw_params_get_sleep_min(swparams, &u);
-      info("sw sleep_min=%lu", (unsigned long)u);
-    }
-#endif
-    snd_pcm_sw_params_get_start_threshold(swparams, &f);
-    info("sw start_threshold=%lu", (unsigned long)f);
-    snd_pcm_sw_params_get_stop_threshold(swparams, &f);
-    info("sw stop_threshold=%lu", (unsigned long)f);
-#if HAVE_SND_PCM_SW_PARAMS_GET_XFER_ALIGN
-    snd_pcm_sw_params_get_xfer_align(swparams, &f);
-    info("sw xfer_align=%lu", (unsigned long)f);
-#endif
-  }
-}
-
-/** @brief ALSA deactivation */
-static void alsa_deactivate(void) {
-  if(pcm) {
-    int err;
-    
-    if((err = snd_pcm_nonblock(pcm, 0)) < 0)
-      fatal(0, "error calling snd_pcm_nonblock: %d", err);
-    D(("draining pcm"));
-    snd_pcm_drain(pcm);
-    D(("closing pcm"));
-    snd_pcm_close(pcm);
-    pcm = 0;
-    device_state = device_closed;
-    D(("released audio device"));
-  }
-}
-
-/** @brief ALSA backend activation */
-static void alsa_activate(void) {
-  if(!pcm) {
-    snd_pcm_hw_params_t *hwparams;
-    snd_pcm_sw_params_t *swparams;
-    snd_pcm_uframes_t pcm_bufsize;
-    int err;
-    int sample_format = 0;
-    unsigned rate;
-
-    D(("snd_pcm_open"));
-    if((err = snd_pcm_open(&pcm,
-                           config->device,
-                           SND_PCM_STREAM_PLAYBACK,
-                           SND_PCM_NONBLOCK))) {
-      error(0, "error from snd_pcm_open: %d", err);
-      goto error;
-    }
-    snd_pcm_hw_params_alloca(&hwparams);
-    D(("set up hw params"));
-    if((err = snd_pcm_hw_params_any(pcm, hwparams)) < 0)
-      fatal(0, "error from snd_pcm_hw_params_any: %d", err);
-    if((err = snd_pcm_hw_params_set_access(pcm, hwparams,
-                                           SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
-      fatal(0, "error from snd_pcm_hw_params_set_access: %d", err);
-    switch(config->sample_format.bits) {
-    case 8:
-      sample_format = SND_PCM_FORMAT_S8;
-      break;
-    case 16:
-      switch(config->sample_format.endian) {
-      case ENDIAN_LITTLE: sample_format = SND_PCM_FORMAT_S16_LE; break;
-      case ENDIAN_BIG: sample_format = SND_PCM_FORMAT_S16_BE; break;
-      default:
-        error(0, "unrecognized byte format %d", config->sample_format.endian);
-        goto fatal;
-      }
-      break;
-    default:
-      error(0, "unsupported sample size %d", config->sample_format.bits);
-      goto fatal;
-    }
-    if((err = snd_pcm_hw_params_set_format(pcm, hwparams,
-                                           sample_format)) < 0) {
-      error(0, "error from snd_pcm_hw_params_set_format (%d): %d",
-            sample_format, err);
-      goto fatal;
-    }
-    rate = config->sample_format.rate;
-    if((err = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, 0)) < 0) {
-      error(0, "error from snd_pcm_hw_params_set_rate (%d): %d",
-            config->sample_format.rate, err);
-      goto fatal;
-    }
-    if(rate != (unsigned)config->sample_format.rate)
-      info("want rate %d, got %u", config->sample_format.rate, rate);
-    if((err = snd_pcm_hw_params_set_channels
-        (pcm, hwparams, config->sample_format.channels)) < 0) {
-      error(0, "error from snd_pcm_hw_params_set_channels (%d): %d",
-            config->sample_format.channels, err);
-      goto fatal;
-    }
-    pcm_bufsize = 3 * FRAMES;
-    if((err = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams,
-                                                     &pcm_bufsize)) < 0)
-      fatal(0, "error from snd_pcm_hw_params_set_buffer_size (%d): %d",
-            3 * FRAMES, err);
-    if(pcm_bufsize != 3 * FRAMES && pcm_bufsize != last_pcm_bufsize)
-      info("asked for PCM buffer of %d frames, got %d",
-           3 * FRAMES, (int)pcm_bufsize);
-    last_pcm_bufsize = pcm_bufsize;
-    if((err = snd_pcm_hw_params(pcm, hwparams)) < 0)
-      fatal(0, "error calling snd_pcm_hw_params: %d", err);
-    D(("set up sw params"));
-    snd_pcm_sw_params_alloca(&swparams);
-    if((err = snd_pcm_sw_params_current(pcm, swparams)) < 0)
-      fatal(0, "error calling snd_pcm_sw_params_current: %d", err);
-    if((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, FRAMES)) < 0)
-      fatal(0, "error calling snd_pcm_sw_params_set_avail_min %d: %d",
-            FRAMES, err);
-    if((err = snd_pcm_sw_params(pcm, swparams)) < 0)
-      fatal(0, "error calling snd_pcm_sw_params: %d", err);
-    D(("acquired audio device"));
-    log_params(hwparams, swparams);
-    device_state = device_open;
-  }
-  return;
-fatal:
-  abandon();
-error:
-  /* We assume the error is temporary and that we'll retry in a bit. */
-  if(pcm) {
-    snd_pcm_close(pcm);
-    pcm = 0;
-    device_state = device_error;
-  }
-  return;
-}
-
-/** @brief Play via ALSA */
-static size_t alsa_play(size_t frames) {
-  snd_pcm_sframes_t pcm_written_frames;
-  int err;
-  
-  pcm_written_frames = snd_pcm_writei(pcm,
-                                      playing->buffer + playing->start,
-                                      frames);
-  D(("actually play %zu frames, wrote %d",
-     frames, (int)pcm_written_frames));
-  if(pcm_written_frames < 0) {
-    switch(pcm_written_frames) {
-    case -EPIPE:                        /* underrun */
-      error(0, "snd_pcm_writei reports underrun");
-      if((err = snd_pcm_prepare(pcm)) < 0)
-        fatal(0, "error calling snd_pcm_prepare: %d", err);
-      return 0;
-    case -EAGAIN:
-      return 0;
-    default:
-      fatal(0, "error calling snd_pcm_writei: %d",
-            (int)pcm_written_frames);
-    }
-  } else
-    return pcm_written_frames;
-}
-
-static int alsa_slots, alsa_nslots = -1;
-
-/** @brief Fill in poll fd array for ALSA */
-static void alsa_beforepoll(int attribute((unused)) *timeoutp) {
-  /* We send sample data to ALSA as fast as it can accept it, relying on
-   * the fact that it has a relatively small buffer to minimize pause
-   * latency. */
-  int retry = 3, err;
-  
-  alsa_slots = fdno;
-  do {
-    retry = 0;
-    alsa_nslots = snd_pcm_poll_descriptors(pcm, &fds[fdno], NFDS - fdno);
-    if((alsa_nslots <= 0
-        || !(fds[alsa_slots].events & POLLOUT))
-       && snd_pcm_state(pcm) == SND_PCM_STATE_XRUN) {
-      error(0, "underrun detected after call to snd_pcm_poll_descriptors()");
-      if((err = snd_pcm_prepare(pcm)))
-        fatal(0, "error calling snd_pcm_prepare: %d", err);
-    } else
-      break;
-  } while(retry-- > 0);
-  if(alsa_nslots >= 0)
-    fdno += alsa_nslots;
-}
-
-/** @brief Process poll() results for ALSA */
-static int alsa_ready(void) {
-  int err;
-
-  unsigned short alsa_revents;
-  
-  if((err = snd_pcm_poll_descriptors_revents(pcm,
-                                             &fds[alsa_slots],
-                                             alsa_nslots,
-                                             &alsa_revents)) < 0)
-    fatal(0, "error calling snd_pcm_poll_descriptors_revents: %d", err);
-  if(alsa_revents & (POLLOUT | POLLERR))
-    return 1;
-  else
-    return 0;
-}
-
-const struct speaker_backend alsa_backend = {
-  BACKEND_ALSA,
-  0,
-  alsa_init,
-  alsa_activate,
-  alsa_play,
-  alsa_deactivate,
-  alsa_beforepoll,
-  alsa_ready
-};
-
-#endif
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-indent-tabs-mode:nil
-End:
-*/
diff --git a/server/speaker-command.c b/server/speaker-command.c
deleted file mode 100644 (file)
index e85d198..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * This file is part of DisOrder
- * Copyright (C) 2005, 2006, 2007 Richard Kettlewell
- * Portions (C) 2007 Mark Wooding
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file server/speaker-command.c
- * @brief Support for @ref BACKEND_COMMAND */
-
-#include "common.h"
-
-#include <unistd.h>
-#include <poll.h>
-#include <errno.h>
-
-#include "configuration.h"
-#include "syscalls.h"
-#include "log.h"
-#include "speaker-protocol.h"
-#include "speaker.h"
-
-/** @brief Pipe to subprocess
- *
- * This is the file descriptor to write to for @ref BACKEND_COMMAND.
- */
-static int cmdfd = -1;
-
-/** @brief poll array slot for @ref cmdfd
- *
- * Set by command_beforepoll().
- */
-static int cmdfd_slot;
-
-/** @brief Start the subprocess for @ref BACKEND_COMMAND */
-static void fork_cmd(void) {
-  pid_t cmdpid;
-  int pfd[2];
-  if(cmdfd != -1) close(cmdfd);
-  xpipe(pfd);
-  cmdpid = xfork();
-  if(!cmdpid) {
-    exitfn = _exit;
-    signal(SIGPIPE, SIG_DFL);
-    xdup2(pfd[0], 0);
-    close(pfd[0]);
-    close(pfd[1]);
-    execl("/bin/sh", "sh", "-c", config->speaker_command, (char *)0);
-    fatal(errno, "error execing /bin/sh");
-  }
-  close(pfd[0]);
-  cmdfd = pfd[1];
-  D(("forked cmd %d, fd = %d", cmdpid, cmdfd));
-}
-
-/** @brief Command backend initialization */
-static void command_init(void) {
-  info("selected command backend");
-  fork_cmd();
-}
-
-/** @brief Play to a subprocess */
-static size_t command_play(size_t frames) {
-  size_t bytes = frames * bpf;
-  int written_bytes;
-
-  written_bytes = write(cmdfd, playing->buffer + playing->start, bytes);
-  D(("actually play %zu bytes, wrote %d",
-     bytes, written_bytes));
-  if(written_bytes < 0) {
-    switch(errno) {
-    case EPIPE:
-      error(0, "hmm, command died; trying another");
-      fork_cmd();
-      return 0;
-    case EAGAIN:
-      return 0;
-    default:
-      fatal(errno, "error writing to subprocess");
-    }
-  } else
-    return written_bytes / bpf;
-}
-
-/** @brief Update poll array for writing to subprocess */
-static void command_beforepoll(int attribute((unused)) *timeoutp) {
-  /* We send sample data to the subprocess as fast as it can accept it.
-   * This isn't ideal as pause latency can be very high as a result. */
-  if(cmdfd >= 0)
-    cmdfd_slot = addfd(cmdfd, POLLOUT);
-}
-
-/** @brief Process poll() results for subprocess play */
-static int command_ready(void) {
-  if(fds[cmdfd_slot].revents & (POLLOUT | POLLERR))
-    return 1;
-  else
-    return 0;
-}
-
-const struct speaker_backend command_backend = {
-  BACKEND_COMMAND,
-  0,
-  command_init,
-  0,                                    /* activate */
-  command_play,
-  0,                                    /* deactivate */
-  command_beforepoll,
-  command_ready
-};
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-indent-tabs-mode:nil
-End:
-*/
diff --git a/server/speaker-coreaudio.c b/server/speaker-coreaudio.c
deleted file mode 100644 (file)
index 42c3a36..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * This file is part of DisOrder
- * Copyright (C) 2007 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file server/speaker-coreaudio.c
- * @brief Support for @ref BACKEND_COREAUDIO
- *
- * Core Audio likes to make callbacks from a separate player thread
- * which then fill in the required number of bytes of audio.  We fit
- * this into the existing architecture by means of a pipe between the
- * threads.
- *
- * We currently only support 16-bit 44100Hz stereo (and enforce this
- * in @ref lib/configuration.c.)  There are some nasty bodges in this
- * code which depend on this and on further assumptions still...
- *
- * @todo support @ref config::device
- */
-
-#include "common.h"
-
-#if HAVE_COREAUDIO_AUDIOHARDWARE_H
-
-#include <poll.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include <CoreAudio/AudioHardware.h>
-
-#include "configuration.h"
-#include "syscalls.h"
-#include "log.h"
-#include "speaker-protocol.h"
-#include "speaker.h"
-
-/** @brief Core Audio Device ID */
-static AudioDeviceID adid;
-
-/** @brief Pipe between main and player threads
- *
- * We'll write samples to pfd[1] and read them from pfd[0].
- */
-static int pfd[2];
-
-/** @brief Slot number in poll array */
-static int pfd_slot;
-
-/** @brief Callback from Core Audio */
-static OSStatus adioproc
-    (AudioDeviceID attribute((unused)) inDevice,
-     const AudioTimeStamp attribute((unused)) *inNow,
-     const AudioBufferList attribute((unused)) *inInputData,
-     const AudioTimeStamp attribute((unused)) *inInputTime,
-     AudioBufferList *outOutputData,
-     const AudioTimeStamp attribute((unused)) *inOutputTime,
-     void attribute((unused)) *inClientData) {
-  UInt32 nbuffers = outOutputData->mNumberBuffers;
-  AudioBuffer *ab = outOutputData->mBuffers;
-
-  while(nbuffers > 0) {
-    float *samplesOut = ab->mData;
-    size_t samplesOutLeft = ab->mDataByteSize / sizeof (float);
-    int16_t input[1024], *ptr;
-    size_t bytes;
-    ssize_t bytes_read;
-    size_t samples;
-
-    while(samplesOutLeft > 0) {
-      /* Read some more data */
-      bytes = samplesOutLeft * sizeof (int16_t);
-      if(bytes > sizeof input)
-       bytes = sizeof input;
-      
-      bytes_read = read(pfd[0], input, bytes);
-      if(bytes_read < 0)
-       switch(errno) {
-       case EINTR:
-         continue;             /* just try again */
-       case EAGAIN:
-         return 0;             /* underrun - just play 0s */
-       default:
-         fatal(errno, "read error in core audio thread");
-       }
-      assert(bytes_read % 4 == 0); /* TODO horrible bodge! */
-      samples = bytes_read / sizeof (int16_t);
-      assert(samples <= samplesOutLeft);
-      ptr = input;
-      samplesOutLeft -= samples;
-      while(samples-- > 0)
-       *samplesOut++ = *ptr++ * (0.5 / 32767);
-    }
-    ++ab;
-    --nbuffers;
-  }
-  return 0;
-}
-
-/** @brief Core Audio backend initialization */
-static void coreaudio_init(void) {
-  OSStatus status;
-  UInt32 propertySize;
-  AudioStreamBasicDescription asbd;
-
-  propertySize = sizeof adid;
-  status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
-                                   &propertySize, &adid);
-  if(status)
-    fatal(0, "AudioHardwareGetProperty: %d", (int)status);
-  if(adid == kAudioDeviceUnknown)
-    fatal(0, "no output device");
-  propertySize = sizeof asbd;
-  status = AudioDeviceGetProperty(adid, 0, false,
-                                 kAudioDevicePropertyStreamFormat,
-                                 &propertySize, &asbd);
-  if(status)
-    fatal(0, "AudioHardwareGetProperty: %d", (int)status);
-  D(("mSampleRate       %f", asbd.mSampleRate));
-  D(("mFormatID         %08lx", asbd.mFormatID));
-  D(("mFormatFlags      %08lx", asbd.mFormatFlags));
-  D(("mBytesPerPacket   %08lx", asbd.mBytesPerPacket));
-  D(("mFramesPerPacket  %08lx", asbd.mFramesPerPacket));
-  D(("mBytesPerFrame    %08lx", asbd.mBytesPerFrame));
-  D(("mChannelsPerFrame %08lx", asbd.mChannelsPerFrame));
-  D(("mBitsPerChannel   %08lx", asbd.mBitsPerChannel));
-  D(("mReserved         %08lx", asbd.mReserved));
-  if(asbd.mFormatID != kAudioFormatLinearPCM)
-    fatal(0, "audio device does not support kAudioFormatLinearPCM");
-  status = AudioDeviceAddIOProc(adid, adioproc, 0);
-  if(status)
-    fatal(0, "AudioDeviceAddIOProc: %d", (int)status);
-  if(socketpair(PF_UNIX, SOCK_STREAM, 0, pfd) < 0)
-    fatal(errno, "error calling socketpair");
-  nonblock(pfd[0]);
-  nonblock(pfd[1]);
-  info("selected Core Audio backend");
-}
-
-/** @brief Core Audio deactivation */
-static void coreaudio_deactivate(void) {
-  const OSStatus status = AudioDeviceStop(adid, adioproc);
-  if(status) {
-    error(0, "AudioDeviceStop: %d", (int)status);
-    device_state = device_error;
-  } else
-    device_state = device_closed;
-}
-
-/** @brief Core Audio backend activation */
-static void coreaudio_activate(void) {
-  const OSStatus status = AudioDeviceStart(adid, adioproc);
-
-  if(status) {
-    error(0, "AudioDeviceStart: %d", (int)status);
-    device_state = device_error;  
-  }
-  device_state = device_open;
-}
-
-/** @brief Play via Core Audio */
-static size_t coreaudio_play(size_t frames) {
-  static size_t leftover;
-
-  size_t bytes = frames * bpf + leftover;
-  ssize_t bytes_written;
-
-  if(leftover)
-    /* There is a partial frame left over from an earlier write.  Try
-     * and finish that off before doing anything else. */
-    bytes = leftover;
-  bytes_written = write(pfd[1], playing->buffer + playing->start, bytes);
-  if(bytes_written < 0)
-    switch(errno) {
-    case EINTR:                        /* interrupted */
-    case EAGAIN:               /* buffer full */
-      return 0;                        /* try later */
-    default:
-      fatal(errno, "error writing to core audio player thread");
-    }
-  if(leftover) {
-    /* We were dealing the leftover bytes of a partial frame */
-    leftover -= bytes_written;
-    return !leftover;
-  }
-  leftover = bytes_written % bpf;
-  return bytes_written / bpf;
-}
-
-/** @brief Fill in poll fd array for Core Audio */
-static void coreaudio_beforepoll(int attribute((unused)) *timeoutp) {
-  pfd_slot = addfd(pfd[1], POLLOUT);
-}
-
-/** @brief Process poll() results for Core Audio */
-static int coreaudio_ready(void) {
-  return !!(fds[pfd_slot].revents & (POLLOUT|POLLERR));
-}
-
-/** @brief Backend definition for Core Audio */
-const struct speaker_backend coreaudio_backend = {
-  BACKEND_COREAUDIO,
-  0,
-  coreaudio_init,
-  coreaudio_activate,
-  coreaudio_play,
-  coreaudio_deactivate,
-  coreaudio_beforepoll,
-  coreaudio_ready
-};
-
-#endif
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-indent-tabs-mode:nil
-End:
-*/
diff --git a/server/speaker-network.c b/server/speaker-network.c
deleted file mode 100644 (file)
index e8a7190..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * This file is part of DisOrder
- * Copyright (C) 2005-2008 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file server/speaker-network.c
- * @brief Support for @ref BACKEND_NETWORK */
-
-#include "common.h"
-
-#include <unistd.h>
-#include <poll.h>
-#include <netdb.h>
-#include <gcrypt.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <net/if.h>
-#include <ifaddrs.h>
-#include <errno.h>
-#include <netinet/in.h>
-
-#include "configuration.h"
-#include "syscalls.h"
-#include "log.h"
-#include "addr.h"
-#include "timeval.h"
-#include "rtp.h"
-#include "ifreq.h"
-#include "speaker-protocol.h"
-#include "speaker.h"
-
-/** @brief Network socket
- *
- * This is the file descriptor to write to for @ref BACKEND_NETWORK.
- */
-static int bfd = -1;
-
-/** @brief RTP timestamp
- *
- * This counts the number of samples played (NB not the number of frames
- * played).
- *
- * The timestamp in the packet header is only 32 bits wide.  With 44100Hz
- * stereo, that only gives about half a day before wrapping, which is not
- * particularly convenient for certain debugging purposes.  Therefore the
- * timestamp is maintained as a 64-bit integer, giving around six million years
- * before wrapping, and truncated to 32 bits when transmitting.
- */
-static uint64_t rtp_time;
-
-/** @brief RTP base timestamp
- *
- * This is the real time correspoding to an @ref rtp_time of 0.  It is used
- * to recalculate the timestamp after idle periods.
- */
-static struct timeval rtp_time_0;
-
-/** @brief RTP packet sequence number */
-static uint16_t rtp_seq;
-
-/** @brief RTP SSRC */
-static uint32_t rtp_id;
-
-/** @brief Error counter */
-static int audio_errors;
-
-/** @brief Network backend initialization */
-static void network_init(void) {
-  struct addrinfo *res, *sres;
-  static const struct addrinfo pref = {
-    .ai_flags = 0,
-    .ai_family = PF_INET,
-    .ai_socktype = SOCK_DGRAM,
-    .ai_protocol = IPPROTO_UDP,
-  };
-  static const struct addrinfo prefbind = {
-    .ai_flags = AI_PASSIVE,
-    .ai_family = PF_INET,
-    .ai_socktype = SOCK_DGRAM,
-    .ai_protocol = IPPROTO_UDP,
-  };
-  static const int one = 1;
-  int sndbuf, target_sndbuf = 131072;
-  socklen_t len;
-  char *sockname, *ssockname;
-
-  res = get_address(&config->broadcast, &pref, &sockname);
-  if(!res) exit(-1);
-  if(config->broadcast_from.n) {
-    sres = get_address(&config->broadcast_from, &prefbind, &ssockname);
-    if(!sres) exit(-1);
-  } else
-    sres = 0;
-  if((bfd = socket(res->ai_family,
-                   res->ai_socktype,
-                   res->ai_protocol)) < 0)
-    fatal(errno, "error creating broadcast socket");
-  if(multicast(res->ai_addr)) {
-    /* Multicasting */
-    switch(res->ai_family) {
-    case PF_INET: {
-      const int mttl = config->multicast_ttl;
-      if(setsockopt(bfd, IPPROTO_IP, IP_MULTICAST_TTL, &mttl, sizeof mttl) < 0)
-        fatal(errno, "error setting IP_MULTICAST_TTL on multicast socket");
-      if(setsockopt(bfd, IPPROTO_IP, IP_MULTICAST_LOOP,
-                    &config->multicast_loop, sizeof one) < 0)
-        fatal(errno, "error setting IP_MULTICAST_LOOP on multicast socket");
-      break;
-    }
-    case PF_INET6: {
-      const int mttl = config->multicast_ttl;
-      if(setsockopt(bfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
-                    &mttl, sizeof mttl) < 0)
-        fatal(errno, "error setting IPV6_MULTICAST_HOPS on multicast socket");
-      if(setsockopt(bfd, IPPROTO_IP, IPV6_MULTICAST_LOOP,
-                    &config->multicast_loop, sizeof (int)) < 0)
-        fatal(errno, "error setting IPV6_MULTICAST_LOOP on multicast socket");
-      break;
-    }
-    default:
-      fatal(0, "unsupported address family %d", res->ai_family);
-    }
-    info("multicasting on %s", sockname);
-  } else {
-    struct ifaddrs *ifs;
-
-    if(getifaddrs(&ifs) < 0)
-      fatal(errno, "error calling getifaddrs");
-    while(ifs) {
-      /* (At least on Darwin) IFF_BROADCAST might be set but ifa_broadaddr
-       * still a null pointer.  It turns out that there's a subsequent entry
-       * for he same interface which _does_ have ifa_broadaddr though... */
-      if((ifs->ifa_flags & IFF_BROADCAST)
-         && ifs->ifa_broadaddr
-         && sockaddr_equal(ifs->ifa_broadaddr, res->ai_addr))
-        break;
-      ifs = ifs->ifa_next;
-    }
-    if(ifs) {
-      if(setsockopt(bfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0)
-        fatal(errno, "error setting SO_BROADCAST on broadcast socket");
-      info("broadcasting on %s (%s)", sockname, ifs->ifa_name);
-    } else
-      info("unicasting on %s", sockname);
-  }
-  len = sizeof sndbuf;
-  if(getsockopt(bfd, SOL_SOCKET, SO_SNDBUF,
-                &sndbuf, &len) < 0)
-    fatal(errno, "error getting SO_SNDBUF");
-  if(target_sndbuf > sndbuf) {
-    if(setsockopt(bfd, SOL_SOCKET, SO_SNDBUF,
-                  &target_sndbuf, sizeof target_sndbuf) < 0)
-      error(errno, "error setting SO_SNDBUF to %d", target_sndbuf);
-    else
-      info("changed socket send buffer size from %d to %d",
-           sndbuf, target_sndbuf);
-  } else
-    info("default socket send buffer is %d",
-         sndbuf);
-  /* We might well want to set additional broadcast- or multicast-related
-   * options here */
-  if(sres && bind(bfd, sres->ai_addr, sres->ai_addrlen) < 0)
-    fatal(errno, "error binding broadcast socket to %s", ssockname);
-  if(connect(bfd, res->ai_addr, res->ai_addrlen) < 0)
-    fatal(errno, "error connecting broadcast socket to %s", sockname);
-  /* Select an SSRC */
-  gcry_randomize(&rtp_id, sizeof rtp_id, GCRY_STRONG_RANDOM);
-}
-
-/** @brief Play over the network */
-static size_t network_play(size_t frames) {
-  struct rtp_header header;
-  struct iovec vec[2];
-  size_t bytes = frames * bpf, written_frames;
-  int written_bytes;
-  /* We transmit using RTP (RFC3550) and attempt to conform to the internet
-   * AVT profile (RFC3551). */
-
-  /* If we're starting then initialize the base time */
-  if(!rtp_time)
-    xgettimeofday(&rtp_time_0, 0);
-  if(idled) {
-    /* There may have been a gap.  Fix up the RTP time accordingly. */
-    struct timeval now;
-    uint64_t delta;
-    uint64_t target_rtp_time;
-
-    /* Find the current time */
-    xgettimeofday(&now, 0);
-    /* Find the number of microseconds elapsed since rtp_time=0 */
-    delta = tvsub_us(now, rtp_time_0);
-    if(delta > UINT64_MAX / 88200)
-      fatal(0, "rtp_time=%"PRIu64" now=%ld.%06ld rtp_time_0=%ld.%06ld delta=%"PRIu64" (%"PRId64")",
-            rtp_time,
-            (long)now.tv_sec, (long)now.tv_usec,
-            (long)rtp_time_0.tv_sec, (long)rtp_time_0.tv_usec,
-            delta, delta);
-    target_rtp_time = (delta * config->sample_format.rate
-                       * config->sample_format.channels) / 1000000;
-    /* Overflows at ~6 years uptime with 44100Hz stereo */
-
-    /* rtp_time is the number of samples we've played.  NB that we play
-     * RTP_AHEAD_MS ahead of ourselves, so it may legitimately be ahead of
-     * the value we deduce from time comparison.
-     *
-     * Suppose we have 1s track started at t=0, and another track begins to
-     * play at t=2s.  Suppose 44100Hz stereo.  We send 1s of audio over the
-     * next (about) one second, giving rtp_time=88200.  rtp_time stops at this
-     * point.
-     *
-     * At t=2s we'll have calculated target_rtp_time=176400.  In this case we
-     * set rtp_time=176400 and the player can correctly conclude that it
-     * should leave 1s between the tracks.
-     *
-     * It's never right to reduce rtp_time, for that would imply packets with
-     * overlapping timestamp ranges, which does not make sense.
-     */
-    target_rtp_time &= ~(uint64_t)1;    /* stereo! */
-    if(target_rtp_time > rtp_time) {
-      /* More time has elapsed than we've transmitted samples.  That implies
-       * we've been 'sending' silence.  */
-      info("advancing rtp_time by %"PRIu64" samples",
-           target_rtp_time - rtp_time);
-      rtp_time = target_rtp_time;
-    } else if(target_rtp_time < rtp_time) {
-      info("would reverse rtp_time by %"PRIu64" samples",
-           rtp_time - target_rtp_time);
-    }
-  }
-  header.vpxcc = 2 << 6;              /* V=2, P=0, X=0, CC=0 */
-  header.seq = htons(rtp_seq++);
-  header.timestamp = htonl((uint32_t)rtp_time);
-  header.ssrc = rtp_id;
-  header.mpt = (idled ? 0x80 : 0x00) | 10;
-  /* 10 = L16 = 16-bit x 2 x 44100KHz.  We ought to deduce this value from
-   * the sample rate (in a library somewhere so that configuration.c can rule
-   * out invalid rates).
-   */
-  idled = 0;
-  if(bytes > NETWORK_BYTES - sizeof header) {
-    bytes = NETWORK_BYTES - sizeof header;
-    /* Always send a whole number of frames */
-    bytes -= bytes % bpf;
-  }
-  /* "The RTP clock rate used for generating the RTP timestamp is independent
-   * of the number of channels and the encoding; it equals the number of
-   * sampling periods per second.  For N-channel encodings, each sampling
-   * period (say, 1/8000 of a second) generates N samples. (This terminology
-   * is standard, but somewhat confusing, as the total number of samples
-   * generated per second is then the sampling rate times the channel
-   * count.)"
-   */
-  vec[0].iov_base = (void *)&header;
-  vec[0].iov_len = sizeof header;
-  vec[1].iov_base = playing->buffer + playing->start;
-  vec[1].iov_len = bytes;
-  do {
-    written_bytes = writev(bfd, vec, 2);
-  } while(written_bytes < 0 && errno == EINTR);
-  if(written_bytes < 0) {
-    error(errno, "error transmitting audio data");
-    ++audio_errors;
-    if(audio_errors == 10)
-      fatal(0, "too many audio errors");
-    return 0;
-  } else
-    audio_errors /= 2;
-  written_bytes -= sizeof (struct rtp_header);
-  written_frames = written_bytes / bpf;
-  /* Advance RTP's notion of the time */
-  rtp_time += written_frames * config->sample_format.channels;
-  return written_frames;
-}
-
-static int bfd_slot;
-
-/** @brief Set up poll array for network play */
-static void network_beforepoll(int *timeoutp) {
-  struct timeval now;
-  uint64_t target_us;
-  uint64_t target_rtp_time;
-  const int64_t samples_per_second = config->sample_format.rate
-                                   * config->sample_format.channels;
-  int64_t lead, ahead_ms;
-  
-  /* If we're starting then initialize the base time */
-  if(!rtp_time)
-    xgettimeofday(&rtp_time_0, 0);
-  /* We send audio data whenever we would otherwise get behind */
-  xgettimeofday(&now, 0);
-  target_us = tvsub_us(now, rtp_time_0);
-  if(target_us > UINT64_MAX / 88200)
-    fatal(0, "rtp_time=%"PRIu64" rtp_time_0=%ld.%06ld now=%ld.%06ld target_us=%"PRIu64" (%"PRId64")\n",
-          rtp_time,
-          (long)rtp_time_0.tv_sec, (long)rtp_time_0.tv_usec,
-          (long)now.tv_sec, (long)now.tv_usec,
-          target_us, target_us);
-  target_rtp_time = (target_us * config->sample_format.rate
-                               * config->sample_format.channels)
-                     / 1000000;
-  /* Lead is how far ahead we are */
-  lead = rtp_time - target_rtp_time;
-  if(lead <= 0)
-    /* We're behind or even, so we'll need to write as soon as we can */
-    bfd_slot = addfd(bfd, POLLOUT);
-  else {
-    /* We've ahead, we can afford to wait a bit even if the IP stack thinks it
-     * can accept more. */
-    ahead_ms = 1000 * lead / samples_per_second;
-    if(ahead_ms < *timeoutp)
-      *timeoutp = ahead_ms;
-  }
-}
-
-/** @brief Process poll() results for network play */
-static int network_ready(void) {
-  if(fds[bfd_slot].revents & (POLLOUT | POLLERR))
-    return 1;
-  else
-    return 0;
-}
-
-const struct speaker_backend network_backend = {
-  BACKEND_NETWORK,
-  0,
-  network_init,
-  0,                                    /* activate */
-  network_play,
-  0,                                    /* deactivate */
-  network_beforepoll,
-  network_ready
-};
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-indent-tabs-mode:nil
-End:
-*/
diff --git a/server/speaker-oss.c b/server/speaker-oss.c
deleted file mode 100644 (file)
index a84d0bb..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * This file is part of DisOrder
- * Copyright (C) 2007, 2008 Richard Kettlewell
- * Portions copyright (C) 2007 Ross Younger
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file server/speaker-oss.c
- * @brief Support for @ref BACKEND_OSS */
-
-#include "common.h"
-
-#if HAVE_SYS_SOUNDCARD_H
-
-#include <unistd.h>
-#include <poll.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <sys/soundcard.h>
-
-#include "configuration.h"
-#include "syscalls.h"
-#include "log.h"
-#include "speaker-protocol.h"
-#include "speaker.h"
-
-/** @brief Current device */
-static int ossfd = -1;
-
-/** @brief OSS backend initialization */
-static void oss_init(void) {
-  info("selected OSS backend");
-}
-
-/** @brief OSS deactivation */
-static void oss_deactivate(void) {
-  if(ossfd != -1) {
-    xclose(ossfd);
-    ossfd = -1;
-    device_state = device_closed;
-    D(("released audio device"));
-  }
-}
-
-/** @brief OSS backend activation */
-static void oss_activate(void) {
-  int stereo, format, rate;
-  const char *device;
-
-  if(ossfd == -1) {
-    /* Try to pick a device */
-    if(!strcmp(config->device, "default")) {
-      if(access("/dev/dsp", W_OK) == 0)
-       device = "/dev/dsp";
-      else if(access("/dev/audio", W_OK) == 0)
-       device = "/dev/audio";
-      else {
-        static int reported;
-        
-        if(!reported) {
-          error(0, "cannot determine default OSS device");
-          reported = 1;
-        }
-        goto failed;
-      }
-    } else
-      device = config->device; /* just believe the user */
-    /* Open the device */
-    if((ossfd = open(device, O_WRONLY, 0)) < 0) {
-      error(errno, "error opening %s", device);
-      goto failed;
-    }
-    /* Set the audio format */
-    stereo = (config->sample_format.channels == 2);
-    if(ioctl(ossfd, SNDCTL_DSP_STEREO, &stereo) < 0) {
-      error(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo);
-      goto failed;
-    }
-    if(config->sample_format.bits == 8)
-      format = AFMT_U8;
-    else if(config->sample_format.bits == 16)
-      format = (config->sample_format.endian == ENDIAN_LITTLE
-               ? AFMT_S16_LE : AFMT_S16_BE);
-    else {
-      error(0, "unsupported sample_format for oss backend"); 
-      goto failed;
-    }
-    if(ioctl(ossfd, SNDCTL_DSP_SETFMT, &format) < 0) {
-      error(errno, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format);
-      goto failed;
-    }
-    rate = config->sample_format.rate;
-    if(ioctl(ossfd, SNDCTL_DSP_SPEED, &rate) < 0) {
-      error(errno, "error calling ioctl SNDCTL_DSP_SPEED %d", rate);
-      goto failed;
-    }
-    if((unsigned)rate != config->sample_format.rate)
-      error(0, "asked for %luHz, got %dHz",
-           (unsigned long)config->sample_format.rate, rate);
-    nonblock(ossfd);
-    device_state = device_open;
-  }
-  return;
-failed:
-  device_state = device_error;
-  if(ossfd >= 0) {
-    xclose(ossfd);
-    ossfd = -1;
-  }
-}
-
-/** @brief Play via OSS */
-static size_t oss_play(size_t frames) {
-  const size_t bytes_to_play = frames * bpf;
-  ssize_t bytes_written;
-
-  bytes_written = write(ossfd, playing->buffer + playing->start,
-                       bytes_to_play);
-  if(bytes_written < 0)
-    switch(errno) {
-    case EINTR:                        /* interruped */
-    case EAGAIN:               /* overrun */
-      return 0;                        /* try again later */
-    default:
-      fatal(errno, "error writing to audio device");
-    }
-  return bytes_written / bpf;
-}
-
-static int oss_slot;
-
-/** @brief Fill in poll fd array for OSS */
-static void oss_beforepoll(int attribute((unused)) *timeoutp) {
-  oss_slot = addfd(ossfd, POLLOUT|POLLERR);
-}
-
-/** @brief Process poll() results for OSS */
-static int oss_ready(void) {
-  return !!(fds[oss_slot].revents & (POLLOUT|POLLERR));
-}
-
-const struct speaker_backend oss_backend = {
-  BACKEND_OSS,
-  0,
-  oss_init,
-  oss_activate,
-  oss_play,
-  oss_deactivate,
-  oss_beforepoll,
-  oss_ready
-};
-
-#endif
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-indent-tabs-mode:nil
-End:
-*/
index 340e995..a2c2e7c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder
- * Copyright (C) 2005-2008 Richard Kettlewell
+ * Copyright (C) 2005-2010 Richard Kettlewell
  * Portions (C) 2007 Mark Wooding
  *
  * This program is free software: you can redistribute it and/or modify
  * process that is about to become disorder-normalize) and plays them in the
  * right order.
  *
- * @b Encodings.  For the <a href="http://www.alsa-project.org/">ALSA</a> API,
- * 8- and 16- bit stereo and mono are supported, with any sample rate (within
- * the limits that ALSA can deal with.)
+ * @b Model.  mainloop() implements a select loop awaiting commands from the
+ * main server, new connections to the speaker socket, and audio data on those
+ * connections.  Each connection starts with a queue ID (with a 32-bit
+ * native-endian length word), allowing it to be referred to in commands from
+ * the server.
+ *
+ * Data read on connections is buffered, up to a limit (currently 1Mbyte per
+ * track).  No attempt is made here to limit the number of tracks, it is
+ * assumed that the main server won't start outrageously many decoders.
+ *
+ * Audio is supplied from this buffer to the uaudio play callback.  Playback is
+ * enabled when a track is to be played and disabled when the its last bytes
+ * have been return by the callback; pause and resume is implemneted the
+ * obvious way.  If the callback finds itself required to play when there is no
+ * playing track it returns dead air.
+ *
+ * To implement gapless playback, the server is notified that a track has
+ * finished slightly early.  @ref SM_PLAY is therefore allowed to arrive while
+ * the previous track is still playing provided an early @ref SM_FINISHED has
+ * been sent for it.
+ *
+ * @b Encodings.  The encodings supported depend entirely on the uaudio backend
+ * chosen.  See @ref uaudio.h, etc.
  *
  * Inbound data is expected to match @c config->sample_format.  In normal use
  * this is arranged by the @c disorder-normalize program (see @ref
  * server/normalize.c).
  *
-7 * @b Garbage @b Collection.  This program deliberately does not use the
+ * @b Garbage @b Collection.  This program deliberately does not use the
  * garbage collector even though it might be convenient to do so.  This is for
  * two reasons.  Firstly some sound APIs use thread threads and we do not want
  * to have to deal with potential interactions between threading and garbage
@@ -63,6 +83,9 @@
 #include <poll.h>
 #include <sys/un.h>
 #include <sys/stat.h>
+#include <pthread.h>
+#include <sys/resource.h>
+#include <gcrypt.h>
 
 #include "configuration.h"
 #include "syscalls.h"
 #include "mem.h"
 #include "speaker-protocol.h"
 #include "user.h"
-#include "speaker.h"
 #include "printf.h"
 #include "version.h"
+#include "uaudio.h"
+
+/** @brief Maximum number of FDs to poll for */
+#define NFDS 1024
+
+/** @brief Number of bytes before end of track to send SM_FINISHED
+ *
+ * Generally set to 1 second.
+ */
+static size_t early_finish;
+
+/** @brief Track structure
+ *
+ * Known tracks are kept in a linked list.  Usually there will be at most two
+ * of these but rearranging the queue can cause there to be more.
+ */
+struct track {
+  /** @brief Next track */
+  struct track *next;
+
+  /** @brief Input file descriptor */
+  int fd;                               /* input FD */
+
+  /** @brief Track ID */
+  char id[24];
+
+  /** @brief Start position of data in buffer */
+  size_t start;
+
+  /** @brief Number of bytes of data in buffer */
+  size_t used;
+
+  /** @brief Set @c fd is at EOF */
+  int eof;
+
+  /** @brief Total number of samples played */
+  unsigned long long played;
+
+  /** @brief Slot in @ref fds */
+  int slot;
+
+  /** @brief Set when playable
+   *
+   * A track becomes playable whenever it fills its buffer or reaches EOF; it
+   * stops being playable when it entirely empties its buffer.  Tracks start
+   * out life not playable.
+   */
+  int playable;
+
+  /** @brief Set when finished
+   *
+   * This is set when we've notified the server that the track is finished.
+   * Once this has happened (typically very late in the track's lifetime) the
+   * track cannot be paused or cancelled.
+   */
+  int finished;
+  
+  /** @brief Input buffer
+   *
+   * 1Mbyte is enough for nearly 6s of 44100Hz 16-bit stereo
+   */
+  char buffer[1048576];
+};
+
+/** @brief Lock protecting data structures
+ *
+ * This lock protects values shared between the main thread and the callback.
+ *
+ * It is held 'all' the time by the main thread, the exceptions being when
+ * called activate/deactivate callbacks and when calling (potentially) slow
+ * system calls (in particular poll(), where in fact the main thread will spend
+ * most of its time blocked).
+ *
+ * The callback holds it when it's running.
+ */
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
 
 /** @brief Linked list of all prepared tracks */
-struct track *tracks;
+static struct track *tracks;
 
-/** @brief Playing track, or NULL */
-struct track *playing;
+/** @brief Playing track, or NULL
+ *
+ * This means the track the speaker process intends to play.  It does not
+ * reflect any other state (e.g. activation of uaudio backend).
+ */
+static struct track *playing;
 
-/** @brief Number of bytes pre frame */
-size_t bpf;
+/** @brief Pending playing track, or NULL
+ *
+ * This means the track the server wants the speaker to play.
+ */
+static struct track *pending_playing;
 
 /** @brief Array of file descriptors for poll() */
-struct pollfd fds[NFDS];
+static struct pollfd fds[NFDS];
 
 /** @brief Next free slot in @ref fds */
-int fdno;
+static int fdno;
 
 /** @brief Listen socket */
 static int listenfd;
 
-static time_t last_report;              /* when we last reported */
-static int paused;                      /* pause status */
+/** @brief Timestamp of last potential report to server */
+static time_t last_report;
 
-/** @brief The current device state */
-enum device_states device_state;
+/** @brief Set when paused */
+static int paused;
 
-/** @brief Set when idled
- *
- * This is set when the sound device is deliberately closed by idle().
- */
-int idled;
+/** @brief Set when back end activated */
+static int activated;
+
+/** @brief Signal pipe back into the poll() loop */
+static int sigpipe[2];
 
 /** @brief Selected backend */
-static const struct speaker_backend *backend;
+static const struct uaudio *backend;
 
 static const struct option options[] = {
   { "help", no_argument, 0, 'h' },
@@ -136,12 +241,11 @@ static void help(void) {
   exit(0);
 }
 
-/** @brief Return the number of bytes per frame in @p format */
-static size_t bytes_per_frame(const struct stream_header *format) {
-  return format->channels * format->bits / 8;
-}
-
-/** @brief Find track @p id, maybe creating it if not found */
+/** @brief Find track @p id, maybe creating it if not found
+ * @param id Track ID to find
+ * @param create If nonzero, create track structure of @p id not found
+ * @return Pointer to track structure or NULL
+ */
 static struct track *findtrack(const char *id, int create) {
   struct track *t;
 
@@ -158,7 +262,10 @@ static struct track *findtrack(const char *id, int create) {
   return t;
 }
 
-/** @brief Remove track @p id (but do not destroy it) */
+/** @brief Remove track @p id (but do not destroy it)
+ * @param id Track ID to remove
+ * @return Track structure or NULL if not found
+ */
 static struct track *removetrack(const char *id) {
   struct track *t, **tt;
 
@@ -170,10 +277,13 @@ static struct track *removetrack(const char *id) {
   return t;
 }
 
-/** @brief Destroy a track */
+/** @brief Destroy a track
+ * @param t Track structure
+ */
 static void destroy(struct track *t) {
   D(("destroy %s", t->id));
-  if(t->fd != -1) xclose(t->fd);
+  if(t->fd != -1)
+    xclose(t->fd);
   free(t);
 }
 
@@ -187,193 +297,93 @@ static void destroy(struct track *t) {
  */
 static int speaker_fill(struct track *t) {
   size_t where, left;
-  int n;
+  int n, rc;
 
   D(("fill %s: eof=%d used=%zu",
      t->id, t->eof, t->used));
-  if(t->eof) return -1;
+  if(t->eof)
+    return -1;
   if(t->used < sizeof t->buffer) {
     /* there is room left in the buffer */
     where = (t->start + t->used) % sizeof t->buffer;
     /* Get as much data as we can */
-    if(where >= t->start) left = (sizeof t->buffer) - where;
-    else left = t->start - where;
+    if(where >= t->start)
+      left = (sizeof t->buffer) - where;
+    else
+      left = t->start - where;
+    pthread_mutex_unlock(&lock);
     do {
       n = read(t->fd, t->buffer + where, left);
     } while(n < 0 && errno == EINTR);
-    if(n < 0) {
-      if(errno != EAGAIN) fatal(errno, "error reading sample stream");
-      return 0;
-    }
-    if(n == 0) {
-      D(("fill %s: eof detected", t->id));
+    pthread_mutex_lock(&lock);
+    if(n < 0 && errno == EAGAIN) {
+      /* EAGAIN means more later */
+      rc = 0;
+    } else if(n <= 0) {
+      /* n=0 means EOF.  n<0 means some error occurred.  We log the error but
+       * otherwise treat it as identical to EOF. */
+      if(n < 0)
+        disorder_error(errno, "error reading sample stream for %s", t->id);
+      else
+        D(("fill %s: eof detected", t->id));
       t->eof = 1;
+      /* A track always becomes playable at EOF; we're not going to see any
+       * more data. */
       t->playable = 1;
-      return -1;
+      rc = -1;
+    } else {
+      t->used += n;
+      /* A track becomes playable when it (first) fills its buffer.  For
+       * 44.1KHz 16-bit stereo this is ~6s of audio data.  The latency will
+       * depend how long that takes to decode (hopefuly not very!) */
+      if(t->used == sizeof t->buffer)
+        t->playable = 1;
+      rc = 0;
     }
-    t->used += n;
-    if(t->used == sizeof t->buffer)
-      t->playable = 1;
   }
-  return 0;
-}
-
-/** @brief Close the sound device
- *
- * This is called to deactivate the output device when pausing, and also by the
- * ALSA backend when changing encoding (in which case the sound device will be
- * immediately reactivated).
- */
-static void idle(void) {
-  D(("idle"));
-  if(backend->deactivate) 
-    backend->deactivate();
-  else
-    device_state = device_closed;
-  idled = 1;
-}
-
-/** @brief Abandon the current track */
-void abandon(void) {
-  struct speaker_message sm;
-
-  D(("abandon"));
-  memset(&sm, 0, sizeof sm);
-  sm.type = SM_FINISHED;
-  strcpy(sm.id, playing->id);
-  speaker_send(1, &sm);
-  removetrack(playing->id);
-  destroy(playing);
-  playing = 0;
-}
-
-/** @brief Enable sound output
- *
- * Makes sure the sound device is open and has the right sample format.  Return
- * 0 on success and -1 on error.
- */
-static void activate(void) {
-  if(backend->activate)
-    backend->activate();
-  else
-    device_state = device_open;
-}
-
-/** @brief Check whether the current track has finished
- *
- * The current track is determined to have finished either if the input stream
- * eded before the format could be determined (i.e. it is malformed) or the
- * input is at end of file and there is less than a frame left unplayed.  (So
- * it copes with decoders that crash mid-frame.)
- */
-static void maybe_finished(void) {
-  if(playing
-     && playing->eof
-     && playing->used < bytes_per_frame(&config->sample_format))
-    abandon();
+  return rc;
 }
 
 /** @brief Return nonzero if we want to play some audio
  *
  * We want to play audio if there is a current track; and it is not paused; and
  * it is playable according to the rules for @ref track::playable.
+ *
+ * We don't allow tracks to be paused if we've already told the server we've
+ * finished them; that would cause such tracks to survive much longer than the
+ * few samples they're supposed to, with report() remaining silent for the
+ * duration.
  */
 static int playable(void) {
   return playing
-         && !paused
+         && (!paused || playing->finished)
          && playing->playable;
 }
 
-/** @brief Play up to @p frames frames of audio
- *
- * It is always safe to call this function.
- * - If @ref playing is 0 then it will just return
- * - If @ref paused is non-0 then it will just return
- * - If @ref device_state != @ref device_open then it will call activate() and
- * return if it it fails.
- * - If there is not enough audio to play then it play what is available.
- *
- * If there are not enough frames to play then whatever is available is played
- * instead.  It is up to mainloop() to ensure that speaker_play() is not called
- * when unreasonably only an small amounts of data is available to play.
- */
-static void speaker_play(size_t frames) {
-  size_t avail_frames, avail_bytes, written_frames;
-  ssize_t written_bytes;
-
-  /* Make sure there's a track to play and it is not paused */
-  if(!playable())
-    return;
-  /* Make sure the output device is open */
-  if(device_state != device_open) {
-    activate(); 
-    if(device_state != device_open)
-      return;
-  }
-  D(("play: play %zu/%zu%s %dHz %db %dc",  frames, playing->used / bpf,
-     playing->eof ? " EOF" : "",
-     config->sample_format.rate,
-     config->sample_format.bits,
-     config->sample_format.channels));
-  /* Figure out how many frames there are available to write */
-  if(playing->start + playing->used > sizeof playing->buffer)
-    /* The ring buffer is currently wrapped, only play up to the wrap point */
-    avail_bytes = (sizeof playing->buffer) - playing->start;
-  else
-    /* The ring buffer is not wrapped, can play the lot */
-    avail_bytes = playing->used;
-  avail_frames = avail_bytes / bpf;
-  /* Only play up to the requested amount */
-  if(avail_frames > frames)
-    avail_frames = frames;
-  if(!avail_frames)
-    return;
-  /* Play it, Sam */
-  written_frames = backend->play(avail_frames);
-  written_bytes = written_frames * bpf;
-  /* written_bytes and written_frames had better both be set and correct by
-   * this point */
-  playing->start += written_bytes;
-  playing->used -= written_bytes;
-  playing->played += written_frames;
-  /* If the pointer is at the end of the buffer (or the buffer is completely
-   * empty) wrap it back to the start. */
-  if(!playing->used || playing->start == (sizeof playing->buffer))
-    playing->start = 0;
-  /* If the buffer emptied out mark the track as unplayably */
-  if(!playing->used && !playing->eof) {
-    error(0, "track buffer emptied");
-    playing->playable = 0;
-  }
-  frames -= written_frames;
-  return;
-}
-
-/* Notify the server what we're up to. */
+/** @brief Notify the server what we're up to */
 static void report(void) {
   struct speaker_message sm;
 
   if(playing) {
+    /* Had better not send a report for a track that the server thinks has
+     * finished, that would be confusing. */
+    if(playing->finished)
+      return;
     memset(&sm, 0, sizeof sm);
     sm.type = paused ? SM_PAUSED : SM_PLAYING;
     strcpy(sm.id, playing->id);
-    sm.data = playing->played / config->sample_format.rate;
+    sm.data = playing->played / (uaudio_rate * uaudio_channels);
     speaker_send(1, &sm);
+    xtime(&last_report);
   }
-  time(&last_report);
-}
-
-static void reap(int __attribute__((unused)) sig) {
-  pid_t cmdpid;
-  int st;
-
-  do
-    cmdpid = waitpid(-1, &st, WNOHANG);
-  while(cmdpid > 0);
-  signal(SIGCHLD, reap);
 }
 
-int addfd(int fd, int events) {
+/** @brief Add a file descriptor to the set to poll() for
+ * @param fd File descriptor
+ * @param events Events to wait for e.g. @c POLLIN
+ * @return Slot number
+ */
+static int addfd(int fd, int events) {
   if(fdno < NFDS) {
     fds[fdno].fd = fd;
     fds[fdno].events = events;
@@ -382,33 +392,82 @@ int addfd(int fd, int events) {
     return -1;
 }
 
-/** @brief Table of speaker backends */
-static const struct speaker_backend *backends[] = {
-#if HAVE_ALSA_ASOUNDLIB_H
-  &alsa_backend,
-#endif
-  &command_backend,
-  &network_backend,
-#if HAVE_COREAUDIO_AUDIOHARDWARE_H
-  &coreaudio_backend,
-#endif
-#if HAVE_SYS_SOUNDCARD_H
-  &oss_backend,
-#endif
-  0
-};
+/** @brief Callback to return some sampled data
+ * @param buffer Where to put sample data
+ * @param max_samples How many samples to return
+ * @param userdata User data
+ * @return Number of samples written
+ *
+ * See uaudio_callback().
+ */
+static size_t speaker_callback(void *buffer,
+                               size_t max_samples,
+                               void attribute((unused)) *userdata) {
+  const size_t max_bytes = max_samples * uaudio_sample_size;
+  size_t provided_samples = 0;
+
+  pthread_mutex_lock(&lock);
+  /* TODO perhaps we should immediately go silent if we've been asked to pause
+   * or cancel the playing track (maybe block in the cancel case and see what
+   * else turns up?) */
+  if(playing) {
+    if(playing->used > 0) {
+      size_t bytes;
+      /* Compute size of largest contiguous chunk.  We get called as often as
+       * necessary so there's no need for cleverness here. */
+      if(playing->start + playing->used > sizeof playing->buffer)
+        bytes = sizeof playing->buffer - playing->start;
+      else
+        bytes = playing->used;
+      /* Limit to what we were asked for */
+      if(bytes > max_bytes)
+        bytes = max_bytes;
+      /* Provide it */
+      memcpy(buffer, playing->buffer + playing->start, bytes);
+      playing->start += bytes;
+      playing->used -= bytes;
+      /* Wrap around to start of buffer */
+      if(playing->start == sizeof playing->buffer)
+        playing->start = 0;
+      /* See if we've reached the end of the track */
+      if(playing->used == 0 && playing->eof) {
+        int ignored = write(sigpipe[1], "", 1);
+        (void) ignored;
+      }
+      provided_samples = bytes / uaudio_sample_size;
+      playing->played += provided_samples;
+    }
+  }
+  /* If we couldn't provide anything at all, play dead air */
+  /* TODO maybe it would be better to block, in some cases? */
+  if(!provided_samples) {
+    memset(buffer, 0, max_bytes);
+    provided_samples = max_samples;
+    if(playing)
+      disorder_info("%zu samples silence, playing->used=%zu",
+                    provided_samples, playing->used);
+    else
+      disorder_info("%zu samples silence, playing=NULL", provided_samples);
+  }
+  pthread_mutex_unlock(&lock);
+  return provided_samples;
+}
 
 /** @brief Main event loop */
 static void mainloop(void) {
   struct track *t;
   struct speaker_message sm;
-  int n, fd, stdin_slot, timeout, listen_slot;
+  int n, fd, stdin_slot, timeout, listen_slot, sigpipe_slot;
 
+  /* Keep going while our parent process is alive */
+  pthread_mutex_lock(&lock);
   while(getppid() != 1) {
+    int force_report = 0;
+
     fdno = 0;
-    /* By default we will wait up to a second before thinking about current
-     * state. */
-    timeout = 1000;
+    /* By default we will wait up to half a second before thinking about
+     * current state. */
+    timeout = 500;
     /* Always ready for commands from the main server. */
     stdin_slot = addfd(0, POLLIN);
     /* Also always ready for inbound connections */
@@ -422,18 +481,6 @@ static void mainloop(void) {
       playing->slot = addfd(playing->fd, POLLIN);
     else if(playing)
       playing->slot = -1;
-    if(playable()) {
-      /* We want to play some audio.  If the device is closed then we attempt
-       * to open it. */
-      if(device_state == device_closed)
-        activate();
-      /* If the device is (now) open then we will wait up until it is ready for
-       * more.  If something went wrong then we should have device_error
-       * instead, but the post-poll code will cope even if it's
-       * device_closed. */
-      if(device_state == device_open)
-        backend->beforepoll(&timeout);
-    }
     /* If any other tracks don't have a full buffer, try to read sample data
      * from them.  We do this last of all, so that if we run out of slots,
      * nothing important can't be monitored. */
@@ -446,27 +493,14 @@ static void mainloop(void) {
         } else
           t->slot = -1;
       }
+    sigpipe_slot = addfd(sigpipe[0], POLLIN);
     /* Wait for something interesting to happen */
+    pthread_mutex_unlock(&lock);
     n = poll(fds, fdno, timeout);
+    pthread_mutex_lock(&lock);
     if(n < 0) {
       if(errno == EINTR) continue;
-      fatal(errno, "error calling poll");
-    }
-    /* Play some sound before doing anything else */
-    if(playable()) {
-      /* We want to play some audio */
-      if(device_state == device_open) {
-        if(backend->ready())
-          speaker_play(3 * FRAMES);
-      } else {
-        /* We must be in _closed or _error, and it should be the latter, but we
-         * cope with either.
-         *
-         * We most likely timed out, so now is a good time to retry.
-         * speaker_play() knows to re-activate the device if necessary.
-         */
-        speaker_play(3 * FRAMES);
-      }
+      disorder_fatal(errno, "error calling poll");
     }
     /* Perhaps a connection has arrived */
     if(fds[listen_slot].revents & POLLIN) {
@@ -478,30 +512,35 @@ static void mainloop(void) {
       if((fd = accept(listenfd, (struct sockaddr *)&addr, &addrlen)) >= 0) {
         blocking(fd);
         if(read(fd, &l, sizeof l) < 4) {
-          error(errno, "reading length from inbound connection");
+          disorder_error(errno, "reading length from inbound connection");
           xclose(fd);
         } else if(l >= sizeof id) {
-          error(0, "id length too long");
+          disorder_error(0, "id length too long");
           xclose(fd);
         } else if(read(fd, id, l) < (ssize_t)l) {
-          error(errno, "reading id from inbound connection");
+          disorder_error(errno, "reading id from inbound connection");
           xclose(fd);
         } else {
           id[l] = 0;
           D(("id %s fd %d", id, fd));
           t = findtrack(id, 1/*create*/);
           if (write(fd, "", 1) < 0)             /* write an ack */
-                       error(errno, "writing ack to inbound connection");
+            disorder_error(errno, "writing ack to inbound connection for %s",
+                           id);
           if(t->fd != -1) {
-            error(0, "%s: already got a connection", id);
+            disorder_error(0, "%s: already got a connection", id);
             xclose(fd);
           } else {
             nonblock(fd);
             t->fd = fd;               /* yay */
           }
+          /* Notify the server that the connection arrived */
+          sm.type = SM_ARRIVED;
+          strcpy(sm.id, id);
+          speaker_send(1, &sm);
         }
       } else
-        error(errno, "accept");
+        disorder_error(errno, "accept");
     }
     /* Perhaps we have a command to process */
     if(fds[stdin_slot].revents & POLLIN) {
@@ -509,52 +548,72 @@ static void mainloop(void) {
        * this won't be the case, so we don't bother looping around to pick them
        * all up. */ 
       n = speaker_recv(0, &sm);
-      /* TODO */
       if(n > 0)
+        /* As a rule we don't send success replies to most commands - we just
+         * force the regular status update to be sent immediately rather than
+         * on schedule. */
        switch(sm.type) {
        case SM_PLAY:
-          if(playing) fatal(0, "got SM_PLAY but already playing something");
+          /* SM_PLAY is only allowed if the server reasonably believes that
+           * nothing is playing */
+          if(playing) {
+            /* If finished isn't set then the server can't believe that this
+             * track has finished */
+            if(!playing->finished)
+              disorder_fatal(0, "got SM_PLAY but already playing something");
+            /* If pending_playing is set then the server must believe that that
+             * is playing */
+            if(pending_playing)
+              disorder_fatal(0, "got SM_PLAY but have a pending playing track");
+          }
          t = findtrack(sm.id, 1);
           D(("SM_PLAY %s fd %d", t->id, t->fd));
           if(t->fd == -1)
-            error(0, "cannot play track because no connection arrived");
-          playing = t;
-          /* We attempt to play straight away rather than going round the loop.
-           * speaker_play() is clever enough to perform any activation that is
-           * required. */
-          speaker_play(3 * FRAMES);
-          report();
+            disorder_error(0,
+                           "cannot play track because no connection arrived");
+          /* TODO as things stand we often report this error message but then
+           * appear to proceed successfully.  Understanding why requires a look
+           * at play.c: we call prepare() which makes the connection in a child
+           * process, and then sends the SM_PLAY in the parent process.  The
+           * latter may well be faster.  As it happens this is harmless; we'll
+           * just sit around sending silence until the decoder connects and
+           * starts sending some sample data.  But is is annoying and ought to
+           * be fixed. */
+          pending_playing = t;
+          /* If nothing is currently playing then we'll switch to the pending
+           * track below so there's no point distinguishing the situations
+           * here. */
          break;
        case SM_PAUSE:
           D(("SM_PAUSE"));
          paused = 1;
-          report();
+          force_report = 1;
           break;
        case SM_RESUME:
           D(("SM_RESUME"));
-          if(paused) {
-            paused = 0;
-            /* As for SM_PLAY we attempt to play straight away. */
-            if(playing)
-              speaker_play(3 * FRAMES);
-          }
-          report();
+          paused = 0;
+          force_report = 1;
          break;
        case SM_CANCEL:
           D(("SM_CANCEL %s", sm.id));
          t = removetrack(sm.id);
          if(t) {
-           if(t == playing) {
-              /* scratching the playing track */
+           if(t == playing || t == pending_playing) {
+              /* Scratching the track that the server believes is playing,
+               * which might either be the actual playing track or a pending
+               * playing track */
               sm.type = SM_FINISHED;
-             playing = 0;
+              if(t == playing)
+                playing = 0;
+              else
+                pending_playing = 0;
             } else {
               /* Could be scratching the playing track before it's quite got
                * going, or could be just removing a track from the queue.  We
                * log more because there's been a bug here recently than because
                * it's particularly interesting; the log message will be removed
                * if no further problems show up. */
-              info("SM_CANCEL for nonplaying track %s", sm.id);
+              disorder_info("SM_CANCEL for nonplaying track %s", sm.id);
               sm.type = SM_STILLBORN;
             }
             strcpy(sm.id, t->id);
@@ -563,18 +622,19 @@ static void mainloop(void) {
             /* Probably scratching the playing track well before it's got
              * going, but could indicate a bug, so we log this as an error. */
             sm.type = SM_UNKNOWN;
-           error(0, "SM_CANCEL for unknown track %s", sm.id);
+           disorder_error(0, "SM_CANCEL for unknown track %s", sm.id);
           }
           speaker_send(1, &sm);
-          report();
+          force_report = 1;
          break;
        case SM_RELOAD:
           D(("SM_RELOAD"));
-         if(config_read(1)) error(0, "cannot read configuration");
-          info("reloaded configuration");
+         if(config_read(1, NULL))
+            disorder_error(0, "cannot read configuration");
+          disorder_info("reloaded configuration");
          break;
        default:
-         error(0, "unknown message type %d", sm.type);
+         disorder_error(0, "unknown message type %d", sm.type);
         }
     }
     /* Read in any buffered data */
@@ -583,14 +643,59 @@ static void mainloop(void) {
          && t->slot != -1
          && (fds[t->slot].revents & (POLLIN | POLLHUP)))
          speaker_fill(t);
-    /* Maybe we finished playing a track somewhere in the above */
-    maybe_finished();
-    /* If we don't need the sound device for now then close it for the benefit
-     * of anyone else who wants it. */
-    if((!playing || paused) && device_state == device_open)
-      idle();
-    /* If we've not reported out state for a second do so now. */
-    if(time(0) > last_report)
+    /* Drain the signal pipe.  We don't care about its contents, merely that it
+     * interrupted poll(). */
+    if(fds[sigpipe_slot].revents & POLLIN) {
+      char buffer[64];
+      int ignored; (void)ignored;
+
+      ignored = read(sigpipe[0], buffer, sizeof buffer);
+    }
+    /* Send SM_FINISHED when we're near the end of the track.
+     *
+     * This is how we implement gapless play; we hope that the SM_PLAY from the
+     * server arrives before the remaining bytes of the track play out.
+     */
+    if(playing
+       && playing->eof
+       && !playing->finished
+       && playing->used <= early_finish) {
+      memset(&sm, 0, sizeof sm);
+      sm.type = SM_FINISHED;
+      strcpy(sm.id, playing->id);
+      speaker_send(1, &sm);
+      playing->finished = 1;
+    }
+    /* When the track is actually finished, deconfigure it */
+    if(playing && playing->eof && !playing->used) {
+      removetrack(playing->id);
+      destroy(playing);
+      playing = 0;
+    }
+    /* Act on the pending SM_PLAY */
+    if(!playing && pending_playing) {
+      playing = pending_playing;
+      pending_playing = 0;
+      force_report = 1;
+    }
+    /* Impose any state change required by the above */
+    if(playable()) {
+      if(!activated) {
+        activated = 1;
+        pthread_mutex_unlock(&lock);
+        backend->activate();
+        pthread_mutex_lock(&lock);
+      }
+    } else {
+      if(activated) {
+        activated = 0;
+        pthread_mutex_unlock(&lock);
+        backend->deactivate();
+        pthread_mutex_lock(&lock);
+      }
+    }
+    /* If we've not reported our state for a second do so now. */
+    if(force_report || xtime(0) > last_report)
       report();
   }
 }
@@ -602,9 +707,10 @@ int main(int argc, char **argv) {
   struct speaker_message sm;
   const char *d;
   char *dir;
+  struct rlimit rl[1];
 
   set_progname(argv);
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "hVc:dDSs", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
@@ -614,7 +720,7 @@ int main(int argc, char **argv) {
     case 'D': debugging = 0; break;
     case 'S': logsyslog = 0; break;
     case 's': logsyslog = 1; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   if((d = getenv("DISORDER_DEBUG_SPEAKER"))) debugging = atoi(d);
@@ -622,32 +728,54 @@ int main(int argc, char **argv) {
     openlog(progname, LOG_PID, LOG_DAEMON);
     log_default = &log_syslog;
   }
-  if(config_read(1)) fatal(0, "cannot read configuration");
-  bpf = bytes_per_frame(&config->sample_format);
+  config_uaudio_apis = uaudio_apis;
+  if(config_read(1, NULL)) disorder_fatal(0, "cannot read configuration");
   /* ignore SIGPIPE */
   signal(SIGPIPE, SIG_IGN);
-  /* reap kids */
-  signal(SIGCHLD, reap);
   /* set nice value */
   xnice(config->nice_speaker);
   /* change user */
   become_mortal();
   /* make sure we're not root, whatever the config says */
-  if(getuid() == 0 || geteuid() == 0) fatal(0, "do not run as root");
-  /* identify the backend used to play */
-  for(n = 0; backends[n]; ++n)
-    if(backends[n]->backend == config->api)
-      break;
-  if(!backends[n])
-    fatal(0, "unsupported api %d", config->api);
-  backend = backends[n];
+  if(getuid() == 0 || geteuid() == 0)
+    disorder_fatal(0, "do not run as root");
+  /* Make sure we can't have more than NFDS files open (it would bust our
+   * poll() array) */
+  if(getrlimit(RLIMIT_NOFILE, rl) < 0)
+    disorder_fatal(errno, "getrlimit RLIMIT_NOFILE");
+  if(rl->rlim_cur > NFDS) {
+    rl->rlim_cur = NFDS;
+    if(setrlimit(RLIMIT_NOFILE, rl) < 0)
+      disorder_fatal(errno, "setrlimit to reduce RLIMIT_NOFILE to %lu",
+            (unsigned long)rl->rlim_cur);
+    disorder_info("set RLIM_NOFILE to %lu", (unsigned long)rl->rlim_cur);
+  } else
+    disorder_info("RLIM_NOFILE is %lu", (unsigned long)rl->rlim_cur);
+  /* gcrypt initialization */
+  if(!gcry_check_version(NULL))
+    disorder_fatal(0, "gcry_check_version failed");
+  gcry_control(GCRYCTL_INIT_SECMEM, 0);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+  /* create a pipe between the backend callback and the poll() loop */
+  xpipe(sigpipe);
+  nonblock(sigpipe[0]);
+  /* set up audio backend */
+  uaudio_set_format(config->sample_format.rate,
+                    config->sample_format.channels,
+                    config->sample_format.bits,
+                    config->sample_format.bits != 8);
+  early_finish = uaudio_sample_size * uaudio_channels * uaudio_rate;
+  /* TODO other parameters! */
+  backend = uaudio_find(config->api);
   /* backend-specific initialization */
-  backend->init();
+  if(backend->configure)
+    backend->configure();
+  backend->start(speaker_callback, NULL);
   /* create the socket directory */
   byte_xasprintf(&dir, "%s/speaker", config->home);
   unlink(dir);                          /* might be a leftover socket */
   if(mkdir(dir, 0700) < 0 && errno != EEXIST)
-    fatal(errno, "error creating %s", dir);
+    disorder_fatal(errno, "error creating %s", dir);
   /* set up the listen socket */
   listenfd = xsocket(PF_UNIX, SOCK_STREAM, 0);
   memset(&addr, 0, sizeof addr);
@@ -655,18 +783,18 @@ int main(int argc, char **argv) {
   snprintf(addr.sun_path, sizeof addr.sun_path, "%s/speaker/socket",
            config->home);
   if(unlink(addr.sun_path) < 0 && errno != ENOENT)
-    error(errno, "removing %s", addr.sun_path);
+    disorder_error(errno, "removing %s", addr.sun_path);
   xsetsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
   if(bind(listenfd, (const struct sockaddr *)&addr, sizeof addr) < 0)
-    fatal(errno, "error binding socket to %s", addr.sun_path);
+    disorder_fatal(errno, "error binding socket to %s", addr.sun_path);
   xlisten(listenfd, 128);
   nonblock(listenfd);
-  info("listening on %s", addr.sun_path);
+  disorder_info("listening on %s", addr.sun_path);
   memset(&sm, 0, sizeof sm);
   sm.type = SM_READY;
   speaker_send(1, &sm);
   mainloop();
-  info("stopped (parent terminated)");
+  disorder_info("stopped (parent terminated)");
   exit(0);
 }
 
diff --git a/server/speaker.h b/server/speaker.h
deleted file mode 100644 (file)
index 0cc0cb8..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * This file is part of DisOrder
- * Copyright (C) 2005-2008 Richard Kettlewell
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-/** @file server/speaker.h
- * @brief Speaker process
- */
-#ifndef SPEAKER_H
-#define SPEAKER_H
-
-#ifdef WORDS_BIGENDIAN
-# define MACHINE_AO_FMT AO_FMT_BIG
-#else
-# define MACHINE_AO_FMT AO_FMT_LITTLE
-#endif
-
-/** @brief Minimum number of frames to try to play at once
- *
- * The main loop will only attempt to play any audio when this many
- * frames are available (or the current track has reached the end).
- * The actual number of frames it attempts to play will often be
- * larger than this (up to three times).
- *
- * For ALSA we request a buffer of three times this size and set the low
- * watermark to this amount.  The goal is then to keep between 1 and 3 times
- * this many frames in play.
- *
- * For other we attempt to play up to three times this many frames per
- * shot.  In practice we will often only send much less than this.
- */
-#define FRAMES 4096
-
-/** @brief Bytes to send per network packet
- *
- * This is the maximum number of bytes we pass to write(2); to determine actual
- * packet sizes, add a UDP header and an IP header (and a link layer header if
- * it's the link layer size you care about).
- *
- * Don't make this too big or arithmetic will start to overflow.
- */
-#define NETWORK_BYTES (1500-8/*UDP*/-40/*IP*/-8/*conservatism*/)
-
-/** @brief Maximum number of FDs to poll for */
-#define NFDS 256
-
-/** @brief Track structure
- *
- * Known tracks are kept in a linked list.  Usually there will be at most two
- * of these but rearranging the queue can cause there to be more.
- */
-struct track {
-  /** @brief Next track */
-  struct track *next;
-
-  /** @brief Input file descriptor */
-  int fd;                               /* input FD */
-
-  /** @brief Track ID */
-  char id[24];
-
-  /** @brief Start position of data in buffer */
-  size_t start;
-
-  /** @brief Number of bytes of data in buffer */
-  size_t used;
-
-  /** @brief Set @c fd is at EOF */
-  int eof;
-
-  /** @brief Total number of frames played */
-  unsigned long long played;
-
-  /** @brief Slot in @ref fds */
-  int slot;
-
-  /** @brief Set when playable
-   *
-   * A track becomes playable whenever it fills its buffer or reaches EOF; it
-   * stops being playable when it entirely empties its buffer.  Tracks start
-   * out life not playable.
-   */
-  int playable;
-  
-  /** @brief Input buffer
-   *
-   * 1Mbyte is enough for nearly 6s of 44100Hz 16-bit stereo
-   */
-  char buffer[1048576];
-};
-
-/** @brief Structure of a backend */
-struct speaker_backend {
-  /** @brief Which backend this is
-   *
-   * @c -1 terminates the list.
-   */
-  int backend;
-
-  /** @brief Flags
-   *
-   * This field is currently not used and must be 0.
-   */
-  unsigned flags;
-  
-  /** @brief Initialization
-   *
-   * Called once at startup.  This is responsible for one-time setup
-   * operations, for instance opening a network socket to transmit to.
-   *
-   * When writing to a native sound API this might @b not imply opening the
-   * native sound device - that might be done by @c activate below.
-   */
-  void (*init)(void);
-
-  /** @brief Activation
-   * @return 0 on success, non-0 on error
-   *
-   * Called to activate the output device.
-   *
-   * On input @ref device_state may be anything.  If it is @ref
-   * device_open then the device is already open but might be using
-   * the wrong sample format.  The device should be reconfigured to
-   * use the right sample format.
-   *
-   * If it is @ref device_error then a retry is underway and an
-   * attempt to recover or re-open the device (with the right sample
-   * format) should be made.
-   *
-   * If it is @ref device_closed then the device should be opened with
-   * the right sample format.
-   *
-   * Some devices are effectively always open and have no error state, in which
-   * case this callback can be NULL.  Note that @ref device_state still
-   * switches between @ref device_open and @ref device_closed in this case.
-   */
-  void (*activate)(void);
-
-  /** @brief Play sound
-   * @param frames Number of frames to play
-   * @return Number of frames actually played
-   *
-   * If an error occurs (and it is not immediately recovered) this
-   * should set @ref device_state to @ref device_error.
-   */
-  size_t (*play)(size_t frames);
-  
-  /** @brief Deactivation
-   *
-   * Called to deactivate the sound device.  This is the inverse of @c
-   * activate above.
-   *
-   * For sound devices that are open all the time and have no error
-   * state, this callback can be NULL.  Note that @ref device_state
-   * still switches between @ref device_open and @ref device_closed in
-   * this case.
-   */
-  void (*deactivate)(void);
-
-  /** @brief Called before poll()
-   * @param timeoutp Pointer to timeout
-   *
-   * Called before the call to poll().
-   *
-   * If desirable, should call addfd() to update the FD array and stash the
-   * slot number somewhere safe.  This will only be called if @ref device_state
-   * is @ref device_open.
-   *
-   * @p timeoutp points to the poll timeout value in milliseconds.  It may be
-   * reduced, but never increased.
-   *
-   * NB you can NOT assume that @c beforepoll is always called before @c play.
-   */
-  void (*beforepoll)(int *timeoutp);
-
-  /** @brief Called after poll()
-   * @return 1 if output device ready for play, 0 otherwise
-   *
-   * Called after the call to poll().  This will only be called if
-   * @ref device_state = @ref device_open.
-   *
-   * The return value should be 1 if the device was ready to play, or
-   * 0 if it was not.
-   */
-  int (*ready)(void);
-};
-
-/** @brief Possible device states */
-enum device_states {
-  /** @brief The device is closed */
-  device_closed,
-
-  /** @brief The device is open and ready to receive sound
-   *
-   * The current device sample format is potentially part of this state.
-   */
-  device_open,
-  
-  /** @brief An error has occurred on the device
-   *
-   * This state is used to ensure that a small interval is left
-   * between retrying the device.  If errors just set @ref
-   * device_closed then the main loop would busy-wait on broken output
-   * devices.
-   *
-   * The current device sample format is potentially part of this state.
-   */
-  device_error
-};
-
-extern enum device_states device_state;
-extern struct track *tracks;
-extern struct track *playing;
-
-extern const struct speaker_backend network_backend;
-extern const struct speaker_backend alsa_backend;
-extern const struct speaker_backend command_backend;
-extern const struct speaker_backend coreaudio_backend;
-extern const struct speaker_backend oss_backend;
-
-extern struct pollfd fds[NFDS];
-extern int fdno;
-extern size_t bpf;
-extern int idled;
-
-int addfd(int fd, int events);
-void abandon(void);
-
-#endif /* SPEAKER_H */
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-indent-tabs-mode:nil
-End:
-*/
index a99474e..b125417 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004, 2005, 2007, 2008 Richard Kettlewell
+ * Copyright (C) 2004, 2005, 2007-2009 Richard Kettlewell
  *
  * 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
  */
 #include "disorder-server.h"
 
+/** @brief Current AF_UNIX socket path */
 static const char *current_unix;
+
+/** @brief Current AF_UNIX socket */
 static int current_unix_fd;
 
-static struct addrinfo *current_listen_addrinfo;
-static int current_listen_fd;
+/** @brief TCP listener definition */
+struct listener {
+  /** @brief Next listener */
+  struct listener *next;
+
+  /** @brief Local socket address */
+  struct sockaddr *sa;
+
+  /** @brief File descriptor */
+  int fd;
+};
 
+/** @brief Current listeners */
+static struct listener *listeners;
+
+/** @brief Current audio API */
+const struct uaudio *api;
+
+/** @brief Quit DisOrder */
 void quit(ev_source *ev) {
-  info("shutting down...");
+  disorder_info("shutting down...");
   quitting(ev);
   trackdb_close();
-  trackdb_deinit();
-  info("exiting");
+  trackdb_deinit(ev);
+  /* Shutdown subprocesses.
+   *
+   * Subprocesses that use ev_child:
+   * - the speaker
+   * - the current rescan
+   * - any decoders
+   * - ...and players
+   * - the track picker
+   * - mail sender
+   * - the deadlock manager
+   *
+   * Subprocesses that don't:
+   * - any normalizers
+   * These are not shut down currently.
+   */
+  ev_child_killall(ev);
+  disorder_info("exiting");
   exit(0);
 }
 
-static void reset_socket(ev_source *ev) {
+/** @brief Create a copy of an @c addrinfo structure */
+static struct sockaddr *copy_sockaddr(const struct addrinfo *addr) {
+  struct sockaddr *sa = xmalloc_noptr(addr->ai_addrlen);
+  memcpy(sa, addr->ai_addr, addr->ai_addrlen);
+  return sa;
+}
+
+/** @brief Create and destroy sockets to set current configuration */
+void reset_sockets(ev_source *ev) {
   const char *new_unix;
-  struct addrinfo *res;
+  struct addrinfo *res, *r;
+  struct listener *l, **ll;
   struct sockaddr_un sun;
-  char *name;
-  
-  static const struct addrinfo pref = {
-    .ai_flags = AI_PASSIVE,
-    .ai_family = PF_INET,
-    .ai_socktype = SOCK_STREAM,
-    .ai_protocol = IPPROTO_TCP,
-  };
 
   /* unix first */
   new_unix = config_get_file("socket");
@@ -56,50 +92,66 @@ static void reset_socket(ev_source *ev) {
       /* stop the old one and remove it from the filesystem */
       server_stop(ev, current_unix_fd);
       if(unlink(current_unix) < 0)
-       fatal(errno, "unlink %s", current_unix);
+       disorder_fatal(errno, "unlink %s", current_unix);
     }
     /* start the new one */
     if(strlen(new_unix) >= sizeof sun.sun_path)
-      fatal(0, "socket path %s is too long", new_unix);
+      disorder_fatal(0, "socket path %s is too long", new_unix);
     memset(&sun, 0, sizeof sun);
     sun.sun_family = AF_UNIX;
     strcpy(sun.sun_path, new_unix);
     if(unlink(new_unix) < 0 && errno != ENOENT)
-      fatal(errno, "unlink %s", new_unix);
+      disorder_fatal(errno, "unlink %s", new_unix);
     if((current_unix_fd = server_start(ev, PF_UNIX, sizeof sun,
                                       (const struct sockaddr *)&sun,
                                       new_unix)) >= 0) {
       current_unix = new_unix;
       if(chmod(new_unix, 0777) < 0)
-       fatal(errno, "error calling chmod %s", new_unix);
+       disorder_fatal(errno, "error calling chmod %s", new_unix);
     } else
       current_unix = 0;
   }
 
   /* get the new listen config */
-  if(config->listen.n)
-    res = get_address(&config->listen, &pref, &name);
+  if(config->listen.af != -1)
+    res = netaddress_resolve(&config->listen, 1, IPPROTO_TCP);
   else
     res = 0;
 
-  if((res && !current_listen_addrinfo)
-     || (current_listen_addrinfo
-        && (!res
-            || addrinfocmp(res, current_listen_addrinfo)))) {
-    /* something has to change */
-    if(current_listen_addrinfo) {
-      /* delete the old listener */
-      server_stop(ev, current_listen_fd);
-      freeaddrinfo(current_listen_addrinfo);
-      current_listen_addrinfo = 0;
+  /* Close any current listeners that aren't required any more */
+  ll = &listeners;
+  while((l = *ll)) {
+    for(r = res; r; r = r->ai_next)
+      if(!sockaddrcmp(r->ai_addr, l->sa))
+       break;
+    if(!r) {
+      /* Didn't find a match, remove this one */
+      server_stop(ev, l->fd);
+      *ll = l->next;
+    } else {
+      /* This address is still wanted */
+      ll = &l->next;
     }
-    if(res) {
-      /* start the new listener */
-      if((current_listen_fd = server_start(ev, res->ai_family, res->ai_addrlen,
-                                          res->ai_addr, name)) >= 0) {
-       current_listen_addrinfo = res;
-       res = 0;
+  }
+
+  /* Open any new listeners that are required */
+  for(r = res; r; r = r->ai_next) {
+    for(l = listeners; l; l = l->next)
+      if(!sockaddrcmp(r->ai_addr, l->sa))
+       break;
+    if(!l) {
+      /* Didn't find a match, need a new listener */
+      int fd = server_start(ev, r->ai_family, r->ai_addrlen, r->ai_addr,
+                           format_sockaddr(r->ai_addr));
+      if(fd >= 0) {
+       l = xmalloc(sizeof *l);
+       l->next = listeners;
+       l->sa = copy_sockaddr(r);
+       l->fd = fd;
+       listeners = l;
       }
+      /* We ignore any failures (though server_start() will have
+       * logged them). */
     }
   }
   /* if res is still set it needs freeing */
@@ -107,33 +159,47 @@ static void reset_socket(ev_source *ev) {
     freeaddrinfo(res);
 }
 
-int reconfigure(ev_source *ev, int reload) {
+/** @brief Reconfigure the server
+ * @param ev Event loop
+ * @param flags Flags
+ * @return As config_read(); 0 on success, -1 if could not (re-)read config
+ */
+int reconfigure(ev_source *ev, unsigned flags) {
   int need_another_rescan = 0;
   int ret = 0;
 
-  D(("reconfigure(%d)", reload));
-  if(reload) {
+  D(("reconfigure(%x)", flags));
+  /* Deconfigure the old audio API if there is one */
+  if(api) {
+    if(api->close_mixer)
+      api->close_mixer();
+    api = NULL;
+  }
+  if(flags & RECONFIGURE_RELOADING) {
+    /* If there's a rescan in progress, cancel it but remember to start a fresh
+     * one after the reload. */
     need_another_rescan = trackdb_rescan_cancel();
-    trackdb_close();
-    if(config_read(1))
+    /* (Re-)read the configuration */
+    if(config_read(1/*server*/, config))
       ret = -1;
     else {
       /* Tell the speaker it needs to reload its config too. */
       speaker_reload();
-      info("%s: installed new configuration", configfile);
+      disorder_info("%s: installed new configuration", configfile);
     }
-    trackdb_open(TRACKDB_NO_UPGRADE);
-  } else
-    /* We only allow for upgrade at startup */
-    trackdb_open(TRACKDB_CAN_UPGRADE);
+  }
+  /* New audio API */
+  api = uaudio_find(config->api);
+  if(api->configure)
+    api->configure();
+  if(api->open_mixer)
+    api->open_mixer();
+  /* If we interrupted a rescan of all the tracks, start a new one */
   if(need_another_rescan)
     trackdb_rescan(ev, 1/*check*/, 0, 0);
-  /* Arrange timeouts for schedule actions */
-  schedule_init(ev);
-  if(!ret) {
-    queue_read();
-    recent_read();
-    reset_socket(ev);
+  if(!ret && !(flags & RECONFIGURE_FIRST)) {
+    /* Open/close sockets */
+    reset_sockets(ev);
   }
   return ret;
 }
@@ -142,5 +208,7 @@ int reconfigure(ev_source *ev, int reload) {
 Local Variables:
 c-basic-offset:2
 comment-column:40
+fill-column:79
+indent-tabs-mode:nil
 End:
 */
index 81c92f4..60e2e39 100644 (file)
@@ -57,7 +57,7 @@ int main(int argc, char **argv) {
 
   set_progname(argv);
   mem_init();
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "hVc:dDSs", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
@@ -67,15 +67,15 @@ int main(int argc, char **argv) {
     case 'D': debugging = 0; break;
     case 'S': logsyslog = 0; break;
     case 's': logsyslog = 1; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   if(logsyslog) {
     openlog(progname, LOG_PID, LOG_DAEMON);
     log_default = &log_syslog;
   }
-  if(config_read(0))
-    fatal(0, "cannot read configuration");
+  if(config_read(0, NULL))
+    disorder_fatal(0, "cannot read configuration");
   trackdb_init(TRACKDB_NO_RECOVER);
   trackdb_open(TRACKDB_NO_UPGRADE);
   stats = trackdb_stats(0);
diff --git a/server/test-config b/server/test-config
new file mode 100644 (file)
index 0000000..8cdd179
--- /dev/null
@@ -0,0 +1,6 @@
+# Dummy config file for decoder testing
+
+api command
+speaker_command true
+sample_format 16b/44100/2
+
index ec84f68..096f97a 100644 (file)
@@ -45,23 +45,24 @@ int main(int argc, char **argv) {
   int n;
   const char *s;
 
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_CTYPE, ""))
+    disorder_fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "hVc:d", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
     case 'V': version("trackname");
     case 'c': configfile = optarg; break;
     case 'd': debugging = 1; break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
-  if(argc - optind < 3) fatal(0, "not enough arguments");
-  if(argc - optind > 3) fatal(0, "too many arguments");
-  if(config_read(0)) fatal(0, "cannot read configuration");
+  if(argc - optind < 3) disorder_fatal(0, "not enough arguments");
+  if(argc - optind > 3) disorder_fatal(0, "too many arguments");
+  if(config_read(0, NULL)) disorder_fatal(0, "cannot read configuration");
   s = trackname_part(argv[optind], argv[optind+1], argv[optind+2]);
-  if(!s) fatal(0, "trackname_part returned NULL");
+  if(!s) disorder_fatal(0, "trackname_part returned NULL");
   xprintf("%s\n", nullcheck(utf82mb(s)));
-  if(fclose(stdout) < 0) fatal(errno, "error closing stdout");
+  if(fclose(stdout) < 0) disorder_fatal(errno, "error closing stdout");
   return 0;
 }
 
index df6c5a9..eda0767 100644 (file)
@@ -1,6 +1,6 @@
 #
 # This file is part of DisOrder.
-# Copyright (C) 2004, 2007, 2008 Richard Kettlewell
+# Copyright (C) 2004, 2007-2009 Richard Kettlewell
 #
 # 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
 #
 
 pkgdata_DATA=slap.ogg scratch.ogg
-noinst_DATA=long.ogg
 
-EXTRA_DIST=${pkgdata_DATA}
-
-%.raw: %.ogg
-       oggdec -b16 -R -o $@ $<
-
-long.ogg: slap.raw
-       cat slap.raw slap.raw slap.raw slap.raw slap.raw slap.raw \
-               slap.raw slap.raw | oggenc -r -o $@ -
-
-clean-local:
-       rm -f long.ogg slap.raw
+EXTRA_DIST=${pkgdata_DATA} \
+       scratch.wav scratch.flac scratch.mp3 scratch.raw long.ogg \
+       scratch-mp3.raw
diff --git a/sounds/long.ogg b/sounds/long.ogg
new file mode 100644 (file)
index 0000000..977f96a
Binary files /dev/null and b/sounds/long.ogg differ
diff --git a/sounds/scratch-mp3.raw b/sounds/scratch-mp3.raw
new file mode 100644 (file)
index 0000000..4078bd6
Binary files /dev/null and b/sounds/scratch-mp3.raw differ
diff --git a/sounds/scratch.flac b/sounds/scratch.flac
new file mode 100644 (file)
index 0000000..4d1df45
Binary files /dev/null and b/sounds/scratch.flac differ
diff --git a/sounds/scratch.mp3 b/sounds/scratch.mp3
new file mode 100644 (file)
index 0000000..ee915a2
Binary files /dev/null and b/sounds/scratch.mp3 differ
diff --git a/sounds/scratch.raw b/sounds/scratch.raw
new file mode 100644 (file)
index 0000000..1564205
Binary files /dev/null and b/sounds/scratch.raw differ
diff --git a/sounds/scratch.wav b/sounds/scratch.wav
new file mode 100644 (file)
index 0000000..28be862
Binary files /dev/null and b/sounds/scratch.wav differ
index b769eaf..1f93947 100644 (file)
@@ -1,6 +1,6 @@
 #
 # This file is part of DisOrder.
-# Copyright (C) 2004-2008 Richard Kettlewell
+# Copyright (C) 2004-2009 Richard Kettlewell
 #
 # 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
@@ -17,7 +17,7 @@
 #
 
 pkgdata_DATA=about.tmpl choose.tmpl playing.tmpl recent.tmpl           \
-            about.tmpl prefs.tmpl help.tmpl error.tmpl                 \
+            prefs.tmpl help.tmpl error.tmpl                    \
             new.tmpl login.tmpl macros.tmpl                            \
             options options.labels                                     \
             options.columns
index 1d63f61..a0842db 100644 (file)
@@ -1,7 +1,7 @@
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
 <!--
 This file is part of DisOrder.
-Copyright (C) 2004-2008 Richard Kettlewell
+Copyright (C) 2004-2009 Richard Kettlewell
 
 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
@@ -61,7 +61,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
   <div class=section>
 
-   <p>Copyright &copy; 2003-2008 <a
+   <p>Copyright &copy; 2003-2009 <a
    href="http://www.greenend.org.uk/rjk/">Richard Kettlewell</a><br>
 
    Portions copyright &copy; 2007 <a
@@ -75,7 +75,10 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
    Drew, Copyright &copy; 2000-2001 Robert Leslie<br>
 
    Portions copyright &copy; 1997-2006 <a
-   href="http://www.fsf.org/">Free Software Foundation, Inc</a>.</p>
+   href="http://www.fsf.org/">Free Software Foundation, Inc</a><br>
+   
+   Portions Copyright &copy; 2000 <a href="http://www.redhat.com">Red Hat,
+   Inc.</a>, Jonathan Blandford <jrb@redhat.com></p>
 
    <p>This program is free software: you can redistribute it and/or modify it
    under the terms of the <a href="http://www.gnu.org/licenses/gpl-3.0.html">GNU
index 320e688..c83fd35 100644 (file)
@@ -1,5 +1,5 @@
 This file is part of DisOrder.
-Copyright (C) 2008 Richard Kettlewell
+Copyright (C) 2008, 2009 Richard Kettlewell
 
 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
@@ -117,7 +117,7 @@ and then redefine macros as desired.
   <a href="http://www.greenend.org.uk/rjk/disorder/"
      title="DisOrder web site">DisOrder
      version @version</a>
-  &copy; 2003-2008 Richard Kettlewell et al
+  &copy; 2003-2009 Richard Kettlewell et al
 </p>}
 
 @# Expand to the time that @id will be played
index 2508499..5563f7f 100644 (file)
@@ -1,6 +1,6 @@
 #
 # This file is part of DisOrder.
-# Copyright (C) 2004, 2005, 2007, 2008 Richard Kettlewell
+# Copyright (C) 2004, 2005, 2007-2009 Richard Kettlewell
 #
 # 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
@@ -26,12 +26,12 @@ disorder_udplog_DEPENDENCIES=../lib/libdisorder.a
 
 TESTS=cookie.py dbversion.py dump.py files.py play.py queue.py \
        recode.py search.py user-upgrade.py user.py aliases.py  \
-       schedule.py
+       schedule.py hashes.py playlists.py
 
 TESTS_ENVIRONMENT=${PYTHON} -u
 
 clean-local:
        rm -rf testroot *.log *.pyc
 
-EXTRA_DIST=dtest.py ${TESTS}
+EXTRA_DIST=dtest.py ${TESTS} fail.py
 CLEANFILES=*.gcda *.gcov *.gcno *.c.html index.html
index 1c62dc5..a5a8be9 100644 (file)
@@ -1,7 +1,7 @@
 #-*-python-*-
 #
 # This file is part of DisOrder.
-# Copyright (C) 2007, 2008 Richard Kettlewell
+# Copyright (C) 2007-2009 Richard Kettlewell
 #
 # 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
@@ -19,7 +19,7 @@
 
 """Utility module used by tests"""
 
-import os,os.path,subprocess,sys,re,time,unicodedata,random,socket
+import os,os.path,subprocess,sys,re,time,unicodedata,random,socket,traceback
 
 def fatal(s):
     """Write an error message and exit"""
@@ -85,7 +85,7 @@ Make track with relative path S exist"""
     trackdir = os.path.dirname(trackpath)
     if not os.path.exists(trackdir):
         os.makedirs(trackdir)
-    copyfile("%s/sounds/long.ogg" % top_builddir, trackpath)
+    copyfile("%s/sounds/long.ogg" % top_srcdir, trackpath)
     # We record the tracks we created so they can be tested against
     # server responses.  We put them into NFC since that's what the server
     # uses internally.
@@ -190,7 +190,7 @@ tracklength *.mp3 disorder-tracklength
 tracklength *.ogg disorder-tracklength
 tracklength *.wav disorder-tracklength
 tracklength *.flac disorder-tracklength
-api network
+api rtp
 broadcast 127.0.0.1 %d
 broadcast_from 127.0.0.1 %d
 mail_sender no.such.user.sorry@greenend.org.uk
@@ -202,10 +202,10 @@ def common_setup():
     os.mkdir(testroot)
     # Choose a port
     global port
-    port = random.randint(49152, 65535)
+    port = random.randint(49152, 65530)
     while not bindable(port + 1):
         print "port %d is not bindable, trying another" % (port + 1)
-        port = random.randint(49152, 65535)
+        port = random.randint(49152, 65530)
     # Log anything sent to that port
     packetlog = "%s/packetlog" % testroot
     subprocess.Popen(["disorder-udplog",
@@ -230,30 +230,35 @@ Start the daemon."""
     print " starting daemon"
     # remove the socket if it exists
     socket = "%s/home/socket" % testroot
-    try:
+    if os.path.exists(socket):
         os.remove(socket)
-    except:
-        pass
     daemon = subprocess.Popen(["disorderd",
                                "--foreground",
                                "--config", "%s/config" % testroot],
                               stderr=errs)
     # Wait for the socket to be created
     waited = 0
+    sleep_resolution = 0.125
     while not os.path.exists(socket):
         rc = daemon.poll()
         if rc is not None:
             print "FATAL: daemon failed to start up"
             sys.exit(1)
-        waited += 1
+        waited += sleep_resolution
+        if sleep_resolution < 1:
+            sleep_resolution *= 2
         if waited == 1:
             print "  waiting for socket..."
         elif waited >= 60:
             print "FATAL: took too long for socket to appear"
             sys.exit(1)
-        time.sleep(1)
+        time.sleep(sleep_resolution)
     if waited > 0:
-        print "  took about %ds for socket to appear" % waited
+        print "  took about %ss for socket to appear" % waited
+    # Wait for root user to be created
+    command(["disorder",
+             "--config", disorder._configfile, "--no-per-user-config",
+             "--wait-for-root"])
 
 def create_user(username="fred", password="fredpass"):
     """create_user(USERNAME, PASSWORD)
@@ -281,26 +286,25 @@ def stop_daemon():
 Stop the daemon if it has not stopped already"""
     global daemon
     if daemon == None:
+        print " (daemon not running)"
         return
     rc = daemon.poll()
     if rc == None:
         print " stopping daemon"
-        disorder.client().shutdown()
+        os.kill(daemon.pid, 15)
         print "  waiting for daemon"
         rc = daemon.wait()
-        print "  daemon has stopped"
+        print "  daemon has stopped (rc=%d)" % rc
     else:
         print "  daemon already stopped"
     daemon = None
-    # Wait a bit for subprocess to finish too, to try to avoid stupid races
-    time.sleep(2)
 
 def run(module=None, report=True):
     """dtest.run(MODULE)
 
     Run the test in MODULE.  This can be a string (in which case the module
     will be imported) or a module object."""
-    global tests
+    global tests, failures
     tests += 1
     # Locate the test module
     if module is None:
@@ -331,8 +335,12 @@ def run(module=None, report=True):
     stdtracks()
     try:
         module.test()
+    except Exception, e:
+        traceback.print_exc(None, sys.stderr)
+        failures += 1
     finally:
         stop_daemon()
+        os.system("ps -ef | grep disorderd")
     if report:
         if failures:
             print " FAILED"
diff --git a/tests/fail.py b/tests/fail.py
new file mode 100755 (executable)
index 0000000..9d7f081
--- /dev/null
@@ -0,0 +1,28 @@
+#! /usr/bin/env python
+#
+# This file is part of DisOrder.
+# Copyright (C) 2009 Richard Kettlewell
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+import dtest
+
+def test():
+    """Create a daemon and fail"""
+    dtest.start_daemon()
+    dtest.create_user()
+    assert True == False, "forcing failure"
+
+if __name__ == '__main__':
+    dtest.run()
diff --git a/tests/hashes.py b/tests/hashes.py
new file mode 100755 (executable)
index 0000000..813951b
--- /dev/null
@@ -0,0 +1,50 @@
+#! /usr/bin/env python
+#
+# This file is part of DisOrder.
+# Copyright (C) 2009 Richard Kettlewell
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+import dtest,disorder,re
+
+def set_auth_algo(h):
+    configpath = "%s/config" % dtest.testroot
+    config = open(configpath, "r").readlines()
+    config = filter(lambda l: not re.match('authorization_algorithm', l),
+                    config)
+    config.append('authorization_algorithm %s\n' % h)
+    open(configpath, "w").write("".join(config))
+
+def test():
+    """Authentication hash tests"""
+    created = False
+    for h in ['sha1', 'SHA1', 'sha256', 'SHA256',
+              'sha384', 'SHA384', 'sha512', "SHA512" ]:
+        print " setting authorization hash to %s" % h
+        set_auth_algo(h)
+        dtest.start_daemon()
+        if not created:
+            dtest.create_user()
+            created = True
+        print " exercising C implementation"
+        dtest.command(["disorder",
+                       "--config", disorder._configfile, "--no-per-user-config",
+                       "--user", "root", "version"])
+        print " exercising Python implementation"
+        c = disorder.client()
+        c.version()
+        dtest.stop_daemon()
+
+if __name__ == '__main__':
+    dtest.run()
index abf1b38..7958ea3 100755 (executable)
@@ -27,6 +27,8 @@ def test():
     c.random_disable()
     assert c.random_enabled() == False
     track = u"%s/Joe Bloggs/First Album/02:Second track.ogg" % dtest.tracks
+    track2 = u"%s/Joe Bloggs/First Album/04:Fourth track.ogg" % dtest.tracks
+    track3 = u"%s/Joe Bloggs/First Album/05:Fifth track.ogg" % dtest.tracks
     print " adding track to queue"
     c.disable()
     assert c.enabled() == False
@@ -62,6 +64,55 @@ def test():
     t = ts[0]
     assert t['submitter'] == u'fred', "check recent entry submitter"
 
+    print " ensuring queue is clear"
+    c.disable()
+    while c.playing() is not None:
+        time.sleep(1)
+    q = c.queue()
+    for qe in q:
+        c.remove(qe["id"])
+
+    print " testing playafter"
+    print "  adding to empty queue"
+    c.playafter(None, [track])
+    q = c.queue()
+    print '\n'.join(map(lambda n: "%d: %s" % (n, q[n]["track"]),
+                        range(0, len(q))))
+    assert len(q) == 1
+    assert q[0]['track'] == track
+    print "  insert at start of queue"
+    c.playafter(None, [track2])
+    q = c.queue()
+    print '\n'.join(map(lambda n: "%d: %s" % (n, q[n]["track"]),
+                        range(0, len(q))))
+    assert len(q) == 2
+    assert q[0]['track'] == track2
+    assert q[1]['track'] == track
+    print "  insert in middle of queue"
+    c.playafter(q[0]['id'], [track3])
+    q = c.queue()
+    print '\n'.join(map(lambda n: "%d: %s" % (n, q[n]["track"]),
+                        range(0, len(q))))
+    assert len(q) == 3
+    assert q[0]['track'] == track2
+    assert q[1]['track'] == track3
+    assert q[2]['track'] == track
+    print "  insert multiple tracks at end of queue"
+    c.playafter(q[2]['id'], [track2, track])
+    q = c.queue()
+    print '\n'.join(map(lambda n: "%d: %s" % (n, q[n]["track"]),
+                        range(0, len(q))))
+    assert len(q) == 5
+    assert q[0]['track'] == track2
+    assert q[1]['track'] == track3
+    assert q[2]['track'] == track
+    assert q[3]['track'] == track2
+    assert q[4]['track'] == track
+
+    print " clearing queue"
+    for qe in q:
+        c.remove(qe["id"])
+
     print " testing scratches"
     retry = False
     scratchlimit = 5
diff --git a/tests/playlists.py b/tests/playlists.py
new file mode 100755 (executable)
index 0000000..0932632
--- /dev/null
@@ -0,0 +1,151 @@
+#! /usr/bin/env python
+#
+# This file is part of DisOrder.
+# Copyright (C) 2008 Richard Kettlewell
+#
+# 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 program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+import dtest,disorder
+
+def test():
+    """Playlist testing"""
+    dtest.start_daemon()
+    dtest.create_user()
+    c = disorder.client()
+    c.random_disable()
+    #
+    print " checking initial playlist set is empty"
+    l = c.playlists()
+    assert l == [], "checking initial playlist set is empty"
+    #
+    print " creating a shared playlist"
+    c.playlist_lock("wibble")
+    c.playlist_set("wibble", ["one", "two", "three"])
+    c.playlist_unlock()
+    print " checking new playlist appears in list"
+    l = c.playlists()
+    assert l == ["wibble"], "checking new playlists"
+    print " checking new playlist contents is as assigned"
+    l = c.playlist_get("wibble")
+    assert l == ["one", "two", "three"], "checking playlist contents"
+    #
+    print " checking new playlist is shared"
+    s = c.playlist_get_share("wibble")
+    assert s == "shared", "checking playlist is shared"
+    #
+    print " checking cannot unshare un-owned playlist"
+    try:
+        c.playlist_set_share("wibble", "private")
+        print "*** should not be able to adjust shared playlist's sharing ***"
+        assert False
+    except disorder.operationError:
+        pass                            # good
+    #
+    print " modifying shared playlist"
+    c.playlist_lock("wibble")
+    c.playlist_set("wibble", ["three", "two", "one"])
+    c.playlist_unlock()
+    print " checking updated playlist contents is as assigned"
+    l = c.playlist_get("wibble")
+    assert l == ["three", "two", "one"], "checking modified playlist contents"
+    #
+    print " creating a private playlist"
+    c.playlist_lock("fred.spong")
+    c.playlist_set("fred.spong", ["a", "b", "c"])
+    c.playlist_unlock()
+    s = c.playlist_get_share("fred.spong")
+    assert s == "private", "owned playlists are private by default"
+    #
+    print " creating a public playlist"
+    c.playlist_lock("fred.foo")
+    c.playlist_set("fred.foo", ["p", "q", "r"])
+    c.playlist_set_share("fred.foo", "public")
+    c.playlist_unlock()
+    s = c.playlist_get_share("fred.foo")
+    assert s == "public", "new playlist is now public"
+    #
+    print " checking fred can see all playlists"
+    l = c.playlists()
+    assert dtest.lists_have_same_contents(l,
+                                          ["fred.spong", "fred.foo", "wibble"]), "playlist list is as expected"
+    #
+    print " adding a second user"
+    c.adduser("bob", "bobpass")
+    d = disorder.client(user="bob", password="bobpass")
+    print " checking bob cannot see fred's private playlist"
+    l = d.playlists()
+    assert dtest.lists_have_same_contents(l,
+                                          ["fred.foo", "wibble"]), "playlist list is as expected"
+    #
+    print " checking bob can read shared and public playlists"
+    d.playlist_get("wibble")
+    d.playlist_get("fred.foo")
+    print " checking bob cannot read private playlist"
+    try:
+        d.playlist_get("fred.spong")
+        print "*** should not be able to read private playlist ***"
+        assert False
+    except disorder.operationError:
+        pass                            # good
+    #
+    print " checking bob cannot modify fred's playlists"
+    try:
+        d.playlist_lock("fred.foo")
+        print "*** should not be able to lock fred's public playlist ***"
+        assert False
+    except disorder.operationError:
+        pass                            # good
+    try:
+        d.playlist_lock("fred.spong")
+        print "*** should not be able to lock fred's private playlist ***"
+        assert False
+    except disorder.operationError:
+        pass                            # good
+    print " checking unlocked playlists cannot be modified"
+    #
+    try:
+        c.playlist_set("wibble", ["a"])
+        print "*** should not be able to modify unlocked playlists ***"
+        assert False
+    except disorder.operationError:
+        pass                            # good
+    #
+    print " deleting playlists"
+    c.playlist_delete("fred.spong")
+    l = c.playlists()
+    assert dtest.lists_have_same_contents(l,
+                                          ["fred.foo", "wibble"])
+    try:
+        d.playlist_delete("fred.foo")
+        print "*** should not be to delete fred's playlist ***"
+        assert False
+    except disorder.operationError:
+        pass                            # good
+    d.playlist_delete("wibble")
+    l = c.playlists()
+    assert l == ["fred.foo"]
+    c.playlist_delete("fred.foo")
+    l = c.playlists()
+    assert l == []
+    try:
+        c.playlist_delete("nonesuch")
+        print "*** should not be to delete nonexistent playlist ***"
+        assert False
+    except disorder.operationError:
+        pass                            # good
+
+if __name__ == '__main__':
+    dtest.run()
index b0ae1fa..25db56d 100755 (executable)
@@ -1,7 +1,7 @@
 #! /usr/bin/env python
 #
 # This file is part of DisOrder.
-# Copyright (C) 2008 Richard Kettlewell
+# Copyright (C) 2008, 2009 Richard Kettlewell
 #
 # 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
@@ -66,6 +66,7 @@ def test():
                                      "schedule-list"]), ""),
     p = next_playing(c)
     assert p["track"] == track, "checking right track played"
+    print " when=%d expected at least %d" % (int(p["when"]), when)
     assert int(p["when"]) >= when, "checking track played at right time"
     assert c.schedule_list() == [], "checking schedule is empty"
     wait_idle(c)
index a27252c..81c0808 100644 (file)
@@ -92,20 +92,20 @@ int main(int argc, char **argv) {
   
   set_progname(argv);
   mem_init();
-  if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "hVo:", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
     case 'V': version();
     case 'o':
       if(!freopen(optarg, "w", stdout))
-       fatal(errno, "%s", optarg);
+       disorder_fatal(errno, "%s", optarg);
       break;
-    default: fatal(0, "invalid option");
+    default: disorder_fatal(0, "invalid option");
     }
   }
   if(optind + 2 != argc)
-    fatal(0, "missing arguments");
+    disorder_fatal(0, "missing arguments");
   a.n = 2;
   a.s = &argv[optind];
   if(!(ai = get_address(&a, &pref, &name)))
@@ -113,7 +113,7 @@ int main(int argc, char **argv) {
   fd = xsocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
   nonblock(fd);
   if(bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
-    fatal(errno, "error binding to %s", name);
+    disorder_fatal(errno, "error binding to %s", name);
   while(getppid() != 1) {
     /* Wait for something to happen.  We don't just block forever in recvfrom()
      * as otherwise we'd never die if the parent terminated uncontrolledly. */
@@ -127,11 +127,11 @@ int main(int argc, char **argv) {
     if(n < 0) {
       if(errno == EINTR || errno == EAGAIN)
        continue;
-      fatal(errno, "%s: recvfrom", name);
+      disorder_fatal(errno, "%s: recvfrom", name);
     }
     if((err = getnameinfo(&sa.sa, len, h, sizeof h, s, sizeof s,
                          NI_NUMERICHOST|NI_NUMERICSERV|NI_DGRAM)))
-      fatal(0, "getnameinfo: %s", gai_strerror(err));
+      disorder_fatal(0, "getnameinfo: %s", gai_strerror(err));
     xprintf("from host %s service %s: %d bytes\n", h, s, n);
     for(i = 0; i < n; i += 16) {
       for(j = i; j < n && j < i + 16; ++j)
@@ -143,7 +143,7 @@ int main(int argc, char **argv) {
        xprintf("%c", buffer[j] < 128 && isprint(buffer[j]) ? buffer[j] : '.');
       xprintf("\n");
       if(fflush(stdout) < 0)
-       fatal(errno, "stdout");
+       disorder_fatal(errno, "stdout");
     }
   }
   return 0;