[hamradio-commits] [direwolf] 01/03: Imported Upstream version 1.0

Iain Learmonth irl-guest at moszumanska.debian.org
Sat Oct 11 18:03:51 UTC 2014


This is an automated email from the git hooks/post-receive script.

irl-guest pushed a commit to annotated tag debian/1.0-1
in repository direwolf.

commit c647d489ea30ed6eaecf3d7a17d059f2ea39882c
Author: Iain R. Learmonth <irl at fsfe.org>
Date:   Sat Oct 11 18:06:39 2014 +0100

    Imported Upstream version 1.0
---
 APRStt-Implementation-Notes.pdf |  Bin 0 -> 1034778 bytes
 CHANGES.txt                     |  167 ++
 LICENSE-dire-wolf.txt           |  281 +++
 LICENSE-other.txt               |    5 +
 Makefile.linux                  |  289 +++
 Makefile.win                    |  326 +++
 Quick-Start-Guide-Windows.pdf   |  Bin 0 -> 1081718 bytes
 Raspberry-Pi-APRS.pdf           |  Bin 0 -> 835647 bytes
 User-Guide.pdf                  |  Bin 0 -> 2728705 bytes
 aclients.c                      |  825 ++++++++
 aprs_tt.c                       | 1436 +++++++++++++
 aprs_tt.h                       |  100 +
 atest.c                         |  447 ++++
 audio.c                         | 1307 ++++++++++++
 audio.h                         |  208 ++
 audio_win.c                     | 1044 ++++++++++
 ax25_pad.c                      | 1722 ++++++++++++++++
 ax25_pad.h                      |  298 +++
 beacon.c                        |  681 ++++++
 beacon.h                        |    4 +
 config.c                        | 2462 ++++++++++++++++++++++
 config.h                        |  130 ++
 decode_aprs.c                   | 3948 +++++++++++++++++++++++++++++++++++
 decode_aprs.h                   |    5 +
 dedupe.c                        |  243 +++
 dedupe.h                        |   10 +
 demod.c                         |  570 +++++
 demod.h                         |   16 +
 demod_9600.c                    |  463 +++++
 demod_9600.h                    |   21 +
 demod_afsk.c                    |  977 +++++++++
 demod_afsk.h                    |    8 +
 digipeater.c                    |  772 +++++++
 digipeater.h                    |   62 +
 direwolf.c                      |  885 ++++++++
 direwolf.conf                   |  592 ++++++
 direwolf.desktop                |   10 +
 direwolf.h                      |   39 +
 dsp.c                           |  248 +++
 dsp.h                           |   10 +
 dtmf.c                          |  411 ++++
 dtmf.h                          |   10 +
 dw-icon.ico                     |  Bin 0 -> 370070 bytes
 dw-icon.png                     |  Bin 0 -> 24142 bytes
 dw-icon.rc                      |    1 +
 dw-start.sh                     |   77 +
 dwgps.c                         |  327 +++
 dwgps.h                         |   15 +
 encode_aprs.c                   |  797 +++++++
 encode_aprs.h                   |   16 +
 fcs_calc.c                      |  108 +
 fcs_calc.h                      |   11 +
 fsk_demod_agc.h                 |    2 +
 fsk_demod_state.h               |  172 ++
 fsk_filters.h                   |    7 +
 fsk_gen_filter.h                |   15 +
 gen_packets.c                   |  729 +++++++
 gen_tone.c                      |  334 +++
 gen_tone.h                      |   16 +
 hdlc_rec.c                      |  513 +++++
 hdlc_rec.h                      |   24 +
 hdlc_rec2.c                     |  661 ++++++
 hdlc_rec2.h                     |   35 +
 hdlc_send.c                     |  215 ++
 hdlc_send.h                     |   10 +
 igate.c                         | 1491 ++++++++++++++
 igate.h                         |   65 +
 kiss.c                          |  923 +++++++++
 kiss.h                          |   21 +
 kiss_frame.c                    |  407 ++++
 kiss_frame.h                    |   47 +
 kissnet.c                       |  671 ++++++
 kissnet.h                       |   21 +
 latlong.c                       |  281 +++
 latlong.h                       |   13 +
 ll2utm.c                        |   55 +
 misc/README-dire-wolf.txt       |    8 +
 misc/strcasestr.c               |   64 +
 misc/strsep.c                   |   22 +
 misc/strtok_r.c                 |  102 +
 morse.c                         |  381 ++++
 multi_modem.c                   |  484 +++++
 multi_modem.h                   |   20 +
 ptt.c                           |  690 +++++++
 ptt.h                           |   23 +
 pttest.c                        |  287 +++
 rdq.c                           |  453 ++++
 rdq.h                           |   28 +
 redecode.c                      |  252 +++
 redecode.h                      |   15 +
 regex/COPYING                   |  340 +++
 regex/COPYING.LIB               |  510 +++++
 regex/INSTALL                   |  463 +++++
 regex/LICENSES                  |  219 ++
 regex/NEWS                      |    0
 regex/README                    |    0
 regex/README-dire-wolf.txt      |    6 +
 regex/re_comp.h                 |   26 +
 regex/regcomp.c                 | 3801 ++++++++++++++++++++++++++++++++++
 regex/regex.c                   |   74 +
 regex/regex.h                   |  580 ++++++
 regex/regex_internal.c          | 1717 ++++++++++++++++
 regex/regex_internal.h          |  769 +++++++
 regex/regexec.c                 | 4333 +++++++++++++++++++++++++++++++++++++++
 rrbb.c                          |  620 ++++++
 rrbb.h                          |  102 +
 server.c                        | 1249 +++++++++++
 server.h                        |   19 +
 symbols-new.txt                 |  409 ++++
 symbols.c                       |  898 ++++++++
 symbols.h                       |   14 +
 symbolsX.txt                    |  364 ++++
 textcolor.c                     |  345 ++++
 textcolor.h                     |   53 +
 tocalls.txt                     |  209 ++
 tq.c                            |  599 ++++++
 tq.h                            |   35 +
 tt_text.c                       |  677 ++++++
 tt_text.h                       |   17 +
 tt_user.c                       |  806 ++++++++
 tt_user.h                       |   10 +
 tune.h                          |    1 +
 udp_test.c                      |  407 ++++
 utm/LatLong-UTMconversion.c     |  190 ++
 utm/LatLong-UTMconversion.h     |   30 +
 utm/README.txt                  |   10 +
 utm/SwissGrid.cpp               |  140 ++
 utm/UTMConversions.cpp          |   39 +
 utm/constants.h                 |   41 +
 utm2ll.c                        |   72 +
 version.h                       |    7 +
 xmit.c                          |  728 +++++++
 xmit.h                          |   24 +
 133 files changed, 52894 insertions(+)

diff --git a/APRStt-Implementation-Notes.pdf b/APRStt-Implementation-Notes.pdf
new file mode 100755
index 0000000..7ac41bc
Binary files /dev/null and b/APRStt-Implementation-Notes.pdf differ
diff --git a/CHANGES.txt b/CHANGES.txt
new file mode 100755
index 0000000..e76dbb6
--- /dev/null
+++ b/CHANGES.txt
@@ -0,0 +1,167 @@
+----------------
+Revision history
+----------------
+
+
+-----------
+Version 1.0a	May 2014
+-----------
+
+* Bug fix:
+
+Beacons sent directly to IGate server had incorrect source address.
+
+
+
+-----------
+Version 1.0	May 2014
+-----------
+
+* New Features:
+
+Received audio can be obtained with a UDP socket or stdin.
+This can be used to take audio from software defined radios
+such as rtl_fm or gqrx.
+
+9600 baud data rate.
+
+New PBEACON and OBEACON configuration options. Previously
+it was necessary to handcraft beacons. 
+
+Less CPU power required for 300 baud.  This is important
+if you want to run a bunch of decoders at the same time
+to tolerate off-frequency HF SSB signals.
+
+Improved support for UTF-8 character set.
+
+Improved troubleshooting display for APRStt macros.
+
+
+
+-----------
+Version 0.9	November 2013
+-----------
+
+* New Features:
+
+Selection of non-default audio device for Linux ALSA.
+
+Simplified audio device set up for Raspberry Pi.
+
+GPIO lines can be used for PTT on suitable Linux systems.
+
+Improved 1200 baud decoder.
+
+Multiple decoders per channel to tolerate HF SSB signals off frequency.
+
+Command line option "-t 0" to disable text colors.
+
+APRStt macros which allow short numeric only touch tone
+sequences to be processed as much longer predefined sequences.
+
+
+
+* Bugs Fixed:
+
+Now works on 64 bit target.
+
+
+
+* New Restriction for Windows version:
+
+Minimum processor is now Pentium 3 or equivalent or later.
+It's possible to run on something older but you will need
+to rebuild it from source.
+
+
+
+
+-----------
+Version 0.8	August 2013
+-----------
+
+* New Features:
+
+Internet Gateway (IGate) including IPv6 support.
+
+Compatibility with YAAC.
+
+Preemptive digipeating option.
+
+KISS TNC should now work with connected AX.25 protocols
+(e.g. AX25 for Linux), not just APRS.
+
+
+
+-----------
+Version 0.7	March 2013
+-----------
+
+* New Features:
+
+Added APRStt gateway capability.  For details, see:
+
+APRStt-Implementation-Notes.pdf
+
+
+
+
+-----------
+Version 0.6
+-----------
+
+
+* New Features:
+
+Improved performance of AFSK demodulator.
+Now decodes 965 frames from Track 2 of WA8LMF�s TNC Test CD.
+
+KISS protocol now available thru a TCP socket.
+Default port is 8001.
+Change it with KISSPORT option in configuration file.
+
+Ability to salvage frames with bad FCS.
+See section mentioning "bad apple" in the user guide.
+Default of fixing 1 bit works well.  
+Fixing more bits not recommended because there is a high
+probability of occasional corrupted data getting thru.
+
+Added AGW "monitor" format messages.
+Now compatible with APRS-TW for telemetry.
+
+
+* Bugs Fixed:
+
+None.
+
+
+
+* Known Problem:
+
+The Linux (but not Cygwin) version eventually hangs if nothing is
+reading from the KISS pseudo terminal.  Some operating system
+queue fills up, the application write blocks, and decoding stops.
+
+
+* Workaround:
+
+If another application is not using the serial KISS interface,
+run this in another window:
+
+	tail -f /tmp/kisstnc
+
+
+-----------
+Version 0.5
+-----------
+
+
+More error checking and messages for invalid APRS data.
+
+
+-----------
+Version 0.4
+-----------
+
+First general availability.
+
diff --git a/LICENSE-dire-wolf.txt b/LICENSE-dire-wolf.txt
new file mode 100755
index 0000000..3a51aab
--- /dev/null
+++ b/LICENSE-dire-wolf.txt
@@ -0,0 +1,281 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  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.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+                     END OF TERMS AND CONDITIONS
+
diff --git a/LICENSE-other.txt b/LICENSE-other.txt
new file mode 100755
index 0000000..1027d22
--- /dev/null
+++ b/LICENSE-other.txt
@@ -0,0 +1,5 @@
+The Windows version of Dire Wolf contains additional
+open source covered by BSD, GPL, and other licenses.
+
+See "regex" and "misc" subdirectories in the source
+distribution for more details.
\ No newline at end of file
diff --git a/Makefile.linux b/Makefile.linux
new file mode 100755
index 0000000..5a2bfec
--- /dev/null
+++ b/Makefile.linux
@@ -0,0 +1,289 @@
+#
+# Makefile for Linux version of Dire Wolf.
+#
+
+all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients
+
+CC = gcc
+
+#
+# The DSP filters can be sped up considerably with the SSE 
+# instructions.  The SSE instructions were introduced in 1999 
+# with the Pentium III series.
+# SSE2 instructions, added in 2000, don't seem to offer any advantage.
+#
+# Let's look at impact of various optimization levels.
+#
+# Benchmark results with Ubuntu gcc version 4.6.3, 32 bit platform.
+# Intel(R) Celeron(R) CPU 2.53GHz.  Appears to have only 32 bit instructions.
+#
+#       seconds options, comments
+#       ------  -----------------
+#          123  -O2     
+#          128  -O3	Slower than -O2 ?
+#          123  -Ofast  (should be same as -O3 -ffastmath)
+#          126  -Ofast -march=pentium
+#           88  -Ofast -msse
+#          108  -Ofast -march=pentium -msse
+#           88  -Ofast -march=pentium3   (this implies -msse)
+#           89  -Ofast -march=pentium3 -msse
+#
+#
+# Raspberry Pi, ARM11 (ARMv6 + VFP2)
+# gcc (Debian 4.6.3-14+rpi1) 4.6.3
+# 
+#       seconds options, comments
+#       ------  -----------------
+#         1015  -O2     
+#          948  -O3	
+#          928  -Ofast  
+#          937  -Ofast -fmpu=vfp   (worse, no option for vfp2)
+# 
+# Are we getting any vectorizing?
+#
+
+
+
+# 
+# Release 0.9 added a new feature than can consume a lot of CPU
+# power:  multiple AFSK demodulators running in parallel.
+# These spend a lot of time spinning around in little loops
+# calculating the sums of products for the DSP filters.
+#
+# When gcc is generating code for a 32 bit x86 target, it
+# assumes the ancient i386 processor.  This is good for
+# portability but bad for performance.
+#
+# The code can run considerably faster by taking advantage of
+# the SSE instructions available in the Pentium 3 or later.
+# Here we find out if the gcc compiler is generating code
+# for the i386.  If so, we add the option to assume we will 
+# have at least a Pentium 3 to run on.
+#
+# When generating code for the x86_64 target, it is automatically
+# assumed that the SSE instructions are available.
+#
+# If you are using gcc version 4.6 or later, you might get a 
+# small improvement by using the new "-Ofast" option that is
+# not available in older compilers.  
+# "-O3" is used here for compatibility with older compilers.
+#
+# You might also get some improvement by using "-march=native"
+# to fine tune the application for your particular type of
+# hardware.
+#
+# If you are planning to distribute the binary version to
+# other people (in some ham radio software collection), avoid
+# fine tuning it for your particular computer.  It could
+# cause compatibility issues for those with older computers.
+#
+
+arch := $(shell echo | gcc -E -dM - | grep __i386__)
+
+ifneq ($(arch),)
+CFLAGS := -DUSE_ALSA -O3 -march=pentium3 -pthread
+else
+CFLAGS := -DUSE_ALSA -O3 -pthread
+endif
+
+
+# Uncomment following lines to enable GPS interface.
+# DO NOT USE THIS.  Still needs more work.
+#CFLAGS += -DENABLE_GPS
+#LDLIBS += -lgps
+
+
+# Name of current directory.
+# Used to generate zip file name for distribution.
+
+z=$(notdir ${CURDIR})
+
+
+# Main application.
+
+direwolf : direwolf.o config.o  demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec.o \
+		hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o \
+		fcs_calc.o ax25_pad.o \
+		decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
+		gen_tone.o audio.o digipeater.o dedupe.o tq.o xmit.o \
+		ptt.o beacon.o dwgps.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \
+		dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o \
+		utm.a
+	$(CC) $(CFLAGS) -o $@ $^ -lpthread -lrt -lasound $(LDLIBS) -lm
+
+
+# Optimization for slow processors.
+
+demod.o : fsk_fast_filter.h
+
+demod_afsk.o : fsk_fast_filter.h
+
+
+fsk_fast_filter.h : demod_afsk.c
+	$(CC) $(CFLAGS) -o gen_fff -DGEN_FFF demod_afsk.c dsp.c textcolor.c -lm
+	./gen_fff > fsk_fast_filter.h
+
+
+
+utm.a : LatLong-UTMconversion.o
+	ar -cr $@ $^
+
+LatLong-UTMconversion.o : utm/LatLong-UTMconversion.c
+	$(CC) $(CFLAGS) -c -o $@ $^
+
+
+# Optional install step. 
+# TODO: Review file locations.
+# TODO: Handle Linux variations correctly.
+# The Raspberry Pi has ~/Desktop but Ubuntu does not.
+# For now, just put reference to it at the end so only last step fails.
+
+install : direwolf decode_aprs tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop
+	sudo install direwolf /usr/local/bin
+	sudo install decode_aprs /usr/local/bin
+	sudo install text2tt /usr/local/bin
+	sudo install tt2text /usr/local/bin
+	sudo install ll2utm /usr/local/bin
+	sudo install utm2ll /usr/local/bin
+	sudo install aclients /usr/local/bin
+	sudo install -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt
+	sudo install -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt
+	sudo install -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt
+	sudo install -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png
+	sudo install -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop
+	cp direwolf.conf ~
+	cp dw-start.sh ~
+	sudo install -D --mode=644 CHANGES.txt /usr/local/share/doc/direwolf/CHANGES.txt
+	sudo install -D --mode=644 LICENSE-dire-wolf.txt /usr/local/share/doc/direwolf/LICENSE-dire-wolf.txt
+	sudo install -D --mode=644 LICENSE-other.txt /usr/local/share/doc/direwolf/LICENSE-other.txt
+	sudo install -D --mode=644 User-Guide.pdf /usr/local/share/doc/direwolf/User-Guide.pdf
+	sudo install -D --mode=644 Raspberry-Pi-APRS.pdf /usr/local/share/doc/direwolf/Raspberry-Pi-APRS.pdf
+	sudo install -D --mode=644 APRStt-Implementation-Notes.pdf /usr/local/share/doc/direwolf/APRStt-Implementation-Notes.pdf
+	sudo install -D --mode=644 Quick-Start-Guide-Windows.pdf /usr/local/share/doc/direwolf/Quick-Start-Guide-Windows.pdf
+	ln -f -s /usr/share/applications/direwolf.desktop ~/Desktop/direwolf.desktop
+
+
+# Separate application to decode raw data.
+
+decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c
+	$(CC) $(CFLAGS) -o decode_aprs -DTEST $^ -lm
+
+
+
+# Convert between text and touch tone representation.
+
+text2tt : tt_text.c
+	$(CC) $(CFLAGS) -DENC_MAIN -o text2tt tt_text.c
+
+tt2text : tt_text.c
+	$(CC) $(CFLAGS) -DDEC_MAIN -o tt2text tt_text.c
+
+
+# Convert between Latitude/Longitude and UTM coordinates.
+
+ll2utm : ll2utm.c utm.a
+	$(CC) $(CFLAGS) -I utm -o $@ $^ -lm
+
+utm2ll : utm2ll.c utm.a
+	$(CC) $(CFLAGS) -I utm -o $@ $^ -lm
+
+
+
+# Test application to generate sound.
+
+gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c textcolor.c 
+	$(CC) $(CFLAGS) -o $@ $^ -lasound -lm
+
+demod.o : tune.h
+demod_afsk.o : tune.h
+demod_9600.o : tune.h
+
+testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o fcs_calc.c ax25_pad.c decode_aprs.c symbols.c tune.h textcolor.c
+	$(CC) $(CFLAGS) -o atest $^ -lm
+	./atest 02_Track_2.wav | grep "packets decoded in" > atest.out
+
+
+# Unit test for AFSK demodulator
+
+
+atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c
+	$(CC) $(CFLAGS) -o $@ $^ -lm
+	time ./atest ../direwolf-0.2/02_Track_2.wav 
+
+# Unit test for inner digipeater algorithm
+
+
+dtest : digipeater.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c
+	$(CC) $(CFLAGS) -DTEST -o $@ $^
+	./dtest
+
+
+# Unit test for IGate
+
+
+itest : igate.c textcolor.c ax25_pad.c fcs_calc.c 
+	$(CC) $(CFLAGS) -DITEST -o $@ $^
+	./itest
+
+
+# Unit test for UDP reception with AFSK demodulator
+
+udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c
+	$(CC) $(CFLAGS) -o $@ $^ -lm -lrt
+	./udptest
+
+
+# Multiple AGWPE network or serial port clients to test TNCs side by side.
+
+aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c
+	$(CC) $(CFLAGS) -g -o $@ $^ 
+
+
+SRCS = direwolf.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c multi_modem.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c \
+		server.c kiss.c kissnet.c kiss_frame.c hdlc_send.c fcs_calc.c gen_tone.c audio.c \
+		digipeater.c dedupe.c tq.c xmit.c beacon.c encode_aprs.c latlong.c encode_aprs.c latlong.c
+
+
+depend : $(SRCS)
+	makedepend $(INCLUDES) $^
+
+ 
+clean :
+	rm -f direwolf decode_aprs text2tt tt2text ll2utm utm2ll fsk_fast_filter.h *.o *.a
+	echo " " > tune.h
+
+
+# Package it up for distribution.
+
+dist-src : CHANGES.txt  User-Guide.pdf Quick-Start-Guide-Windows.pdf Raspberry-Pi-APRS.pdf \
+		direwolf.desktop dw-start.sh 
+	rm -f fsk_fast_filter.h
+	echo " " > tune.h
+	rm -f ../$z-src.zip
+	(cd .. ; zip $z-src.zip $z/CHANGES.txt $z/LICENSE* \
+		$z/User-Guide.pdf $z/Quick-Start-Guide-Windows.pdf $z/Raspberry-Pi-APRS.pdf \
+		$z/Makefile* $z/*.c $z/*.h $z/regex/* $z/misc/* $z/utm/* \
+		$z/*.conf $z/tocalls.txt $z/symbols-new.txt $z/symbolsX.txt \
+		$z/dw-icon.png $z/dw-icon.rc $z/dw-icon.ico \
+		$z/direwolf.desktop $z/dw-start.sh )
+
+
+#User-Guide.pdf : User-Guide.docx
+#	echo "***** User-Guide.pdf is out of date *****"
+
+#Quick-Start-Guide-Windows.pdf : Quick-Start-Guide-Windows.docx
+#	echo "***** Quick-Start-Guide-Windows.pdf is out of date *****" 
+
+#Raspberry-Pi-APRS.pdf : Raspberry-Pi-APRS.docx
+#	echo "***** Raspberry-Pi-APRS.pdf is out of date *****" 
+
+
+backup :
+	mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
+	cp -r . /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
+
+#
+# The following is updated by "make depend"
+#
+# DO NOT DELETE
+
diff --git a/Makefile.win b/Makefile.win
new file mode 100755
index 0000000..b879533
--- /dev/null
+++ b/Makefile.win
@@ -0,0 +1,326 @@
+#
+# Makefile for native Windows version of Dire Wolf.
+#
+#
+# This is built in the Cygwin environment but with the 
+# compiler from http://www.mingw.org/ so there is no
+# dependency on extra DLLs.
+#
+# The MinGW/bin directory must be in the PATH for the 
+# compiler.  e.g.   export PATH=/cygdrive/c/MinGW/bin:$PATH
+#
+# Failure to have the path set correctly will result in the
+# obscure message:     Makefile.win:... recipe for target ... failed. 
+#
+# Type "which gcc" to make sure you are getting the right one!
+#
+
+
+all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients
+
+
+# People say we need -mthreads option for threads to work properly.
+# They also say it creates a dependency on mingwm10.dll but I'm not seeing that.
+
+#TODO: put -Ofast back in.
+
+CC = gcc
+#CFLAGS = -g -Wall -Ofast -march=pentium3 -msse -Iregex -mthreads -DUSE_REGEX_STATIC
+CFLAGS = -g -Wall -march=pentium3 -msse -Iregex -mthreads -DUSE_REGEX_STATIC
+AR = ar
+
+
+#
+# Let's see impact of various optimization levels.
+# Benchmark results with MinGW gcc version 4.6.2.
+#
+#	seconds	options, comments
+#	------	-----------------
+#	119.8	-O2	Used for version 0.8
+#	 92.1	-O3
+#	 88.7	-Ofast  (should be same as -O3 -ffastmath)
+#	 87.5	-Ofast -march=pentium
+#	 74.1	-Ofast -msse
+#	 72.2	-Ofast -march=pentium -msse
+#	 62.0	-Ofast -march=pentium3   (this implies -msse)
+#	 61.9	-Ofast -march=pentium3 -msse
+#
+# A minimum of Windows XP is required due to some of the system
+# features being used.  XP requires a Pentium processor or later.
+# The DSP filters can be sped up considerably with the SSE instructions.
+# The SSE instructions were introduced in 1999 with the 
+# Pentium III series.  
+# SSE2 instructions, added in 2000, don't seem to offer any advantage.
+#
+# For version 0.9, a Pentium 3 or equivalent is now the minimum required
+# for the prebuilt Windows distribution.
+# If you insist on using a computer from the previous century,
+# you can compile this yourself with different options.
+#
+
+# Name of zip file for distribution.
+
+z=$(notdir ${CURDIR})
+
+
+
+# Main application.
+
+demod.o : fsk_demod_state.h
+demod_9600.o : fsk_demod_state.h
+demod_afsk.o : fsk_demod_state.h
+
+
+direwolf : direwolf.o config.o demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec.o \
+		hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o \
+		fcs_calc.o ax25_pad.o \
+		decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
+		gen_tone.o audio_win.o digipeater.o dedupe.o tq.o xmit.o \
+		ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \
+		dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o \
+		dw-icon.o regex.a misc.a utm.a
+	$(CC) $(CFLAGS) -g -o $@ $^ -lwinmm -lws2_32
+
+dw-icon.o : dw-icon.rc dw-icon.ico
+	windres dw-icon.rc -o $@
+
+
+# Optimization for slow processors.
+
+demod.o : fsk_fast_filter.h
+
+demod_afsk.o : fsk_fast_filter.h
+
+
+fsk_fast_filter.h : demod_afsk.c
+	$(CC) $(CFLAGS) -o gen_fff -DGEN_FFF demod_afsk.c dsp.c textcolor.c
+	./gen_fff > fsk_fast_filter.h
+
+
+utm.a : LatLong-UTMconversion.o
+	ar -cr $@ $^
+
+LatLong-UTMconversion.o : utm/LatLong-UTMconversion.c
+	$(CC) $(CFLAGS) -c -o $@ $^
+
+
+#
+# When building for Linux, we use regular expression
+# functions supplied by the gnu C library.
+# For the native WIN32 version, we need to use our own copy.
+# These were copied from http://gnuwin32.sourceforge.net/packages/regex.htm
+#
+
+regex.a : regex.o
+	ar -cr $@ $^	
+ 
+regex.o : regex/regex.c
+	$(CC) $(CFLAGS) -Dbool=int -Dtrue=1 -Dfalse=0 -c -o $@ $^
+
+
+# There are also a couple other functions in the misc
+# subdirectory that are missing on Windows.
+
+misc.a : strsep.o strtok_r.o strcasestr.o
+	ar -cr $@ $^	
+ 
+strsep.o : misc/strsep.c
+	$(CC) $(CFLAGS) -c -o $@ $^
+
+strtok_r.o : misc/strtok_r.c
+	$(CC) $(CFLAGS) -c -o $@ $^
+
+strcasestr.o : misc/strcasestr.c
+	$(CC) $(CFLAGS) -c -o $@ $^
+
+
+
+# Separate application to decode raw data.
+
+decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c regex.a misc.a
+	$(CC) $(CFLAGS) -o decode_aprs -DTEST $^
+
+
+# Convert between text and touch tone representation.
+
+text2tt : tt_text.c
+	$(CC) $(CFLAGS) -DENC_MAIN -o text2tt tt_text.c
+
+tt2text : tt_text.c
+	$(CC) $(CFLAGS) -DDEC_MAIN -o tt2text tt_text.c
+
+
+# Convert between Latitude/Longitude and UTM coordinates.
+
+ll2utm : ll2utm.c utm.a
+	$(CC) $(CFLAGS) -I utm -o $@ $^
+
+utm2ll : utm2ll.c utm.a
+	$(CC) $(CFLAGS) -I utm -o $@ $^
+
+
+# Test application to generate sound.
+
+gen_packets : gen_packets.o  ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o textcolor.o misc.a regex.a
+	$(CC) $(CFLAGS) -o $@ $^
+
+# For tweaking the demodulator.
+
+demod.o : tune.h
+demod_9600.o : tune.h
+demod_afsk.o : tune.h
+
+
+testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
+		rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c regex.a misc.a \
+		fsk_demod_agc.h
+	rm -f atest.exe
+	$(CC) $(CFLAGS) -DNOFIX -o atest $^
+	./atest ../direwolf-0.2/02_Track_2.wav | grep "packets decoded in" >atest.out
+
+
+noisy3.wav : gen_packets
+	./gen_packets -B 300 -n 100 -o noisy3.wav
+
+testagc3 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
+		rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c regex.a misc.a \
+		tune.h 
+	rm -f atest.exe
+	$(CC) $(CFLAGS) -o atest $^
+	./atest -B 300 -P D -D 3 noisy3.wav | grep "packets decoded in" >atest.out
+
+
+noisy96.wav : gen_packets
+	./gen_packets -B 9600 -n 100 -o noisy96.wav
+
+testagc9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
+		rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c regex.a misc.a \
+		tune.h 
+	rm -f atest.exe
+	$(CC) $(CFLAGS) -o atest $^
+	./atest -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out
+	#./atest -B 9600 noisy96.wav | grep "packets decoded in" >atest.out
+
+
+# Unit test for AFSK demodulator
+
+
+atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
+		rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c misc.a regex.a \
+		fsk_fast_filter.h
+	$(CC) $(CFLAGS) -o $@ $^
+	echo " " > tune.h
+	./atest ..\\direwolf-0.2\\02_Track_2.wav 
+
+atest9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
+		rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c misc.a regex.a \
+		fsk_fast_filter.h
+	$(CC) $(CFLAGS) -o $@ $^
+	./atest9 -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out
+	#./atest9 -B 9600 noise96.wav 
+
+
+# Unit test for inner digipeater algorithm
+
+
+dtest : digipeater.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c misc.a regex.a
+	$(CC) $(CFLAGS) -DTEST -o $@ $^
+	./dtest
+	rm dtest.exe
+
+# Unit test for APRStt.
+
+ttest : aprs_tt.c tt_text.c  misc.a utm.a
+	$(CC) $(CFLAGS) -DTT_MAIN  -o ttest aprs_tt.c tt_text.c  misc.a utm.a
+
+
+# Unit test for IGate
+
+itest : igate.c textcolor.c ax25_pad.c fcs_calc.c misc.a regex.a
+	$(CC) $(CFLAGS) -DITEST -g -o $@ $^ -lwinmm -lws2_32
+
+
+# Unit test for UDP reception with AFSK demodulator
+
+udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c
+	$(CC) $(CFLAGS) -o $@ $^ -lm -lrt
+	./udptest
+
+
+# Multiple AGWPE network or serial port clients to test TNCs side by side.
+
+aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a
+	$(CC) $(CFLAGS) -g -o $@ $^ -lwinmm -lws2_32
+
+
+SRCS = direwolf.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c \
+		hdlc_rec2.c multi_modem.c redecode.c rdq.c rrbb.c \
+		fcs_calc.c ax25_pad.c decode_aprs.c symbols.c \
+		server.c kiss.c kissnet.c kiss_frame.c hdlc_send.c fcs_calc.c gen_tone.c audio_win.c \
+		digipeater.c dedupe.c tq.c xmit.c beacon.c \
+		encode_aprs.c latlong.c \
+		dtmf.c aprs_tt.c tt_text.c igate.c
+
+
+depend : $(SRCS)
+	makedepend $(INCLUDES) $^
+
+ 
+clean :
+	rm -f *.o *.a *.exe fsk_fast_filter.h noisy96.wav
+	echo " " > tune.h
+
+
+# Package it up for distribution:  Prebuilt Windows & source versions.
+
+
+dist-win : direwolf.exe decode_aprs.exe CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf \
+			Raspberry-Pi-APRS.pdf APRStt-Implementation-Notes.pdf
+	rm -f ../$z-win.zip
+	zip ../$z-win.zip CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf \
+		Raspberry-Pi-APRS.pdf APRStt-Implementation-Notes.pdf LICENSE* *.conf \
+		direwolf.exe decode_aprs.exe tocalls.txt symbols-new.txt symbolsX.txt \
+		text2tt.exe tt2text.exe ll2utm.exe utm2ll.exe aclients.exe
+
+dist-src : CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf Raspberry-Pi-APRS.pdf \
+		APRStt-Implementation-Notes.pdf \
+		direwolf.desktop dw-start.sh \
+		tocalls.txt symbols-new.txt symbolsX.txt
+	rm -f fsk_fast_filter.h
+	echo " " > tune.h
+	rm -f ../$z-src.zip
+	(cd .. ; zip $z-src.zip \
+		$z/CHANGES.txt $z/LICENSE* \
+		$z/User-Guide.pdf $z/Quick-Start-Guide-Windows.pdf \
+		$z/Raspberry-Pi-APRS.pdf $z/APRStt-Implementation-Notes.pdf \
+		$z/Makefile* $z/*.c $z/*.h $z/regex/* $z/misc/* $z/utm/* \
+		$z/*.conf $z/tocalls.txt $z/symbols-new.txt $z/symbolsX.txt \
+		$z/dw-icon.png $z/dw-icon.rc $z/dw-icon.ico \
+		$z/direwolf.desktop $z/dw-start.sh )
+		
+
+
+User-Guide.pdf : User-Guide.docx
+	echo "***** User-Guide.pdf is out of date *****"
+
+Quick-Start-Guide-Windows.pdf : Quick-Start-Guide-Windows.docx
+	echo "***** Quick-Start-Guide-Windows.pdf is out of date *****" 
+
+Raspberry-Pi-APRS.pdf : Raspberry-Pi-APRS.docx
+	echo "***** Raspberry-Pi-APRS.pdf is out of date *****" 
+
+APRStt-Implementation-Notes.pdf : APRStt-Implementation-Notes.docx
+	echo "***** APRStt-Implementation-Notes.pdf is out of date *****"
+
+
+backup :
+	mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
+	cp -r . /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
+
+#
+# The following is updated by "make depend"
+#
+# DO NOT DELETE
+
+
+
diff --git a/Quick-Start-Guide-Windows.pdf b/Quick-Start-Guide-Windows.pdf
new file mode 100755
index 0000000..6d155f0
Binary files /dev/null and b/Quick-Start-Guide-Windows.pdf differ
diff --git a/Raspberry-Pi-APRS.pdf b/Raspberry-Pi-APRS.pdf
new file mode 100755
index 0000000..ef01cce
Binary files /dev/null and b/Raspberry-Pi-APRS.pdf differ
diff --git a/User-Guide.pdf b/User-Guide.pdf
new file mode 100755
index 0000000..ff3b46f
Binary files /dev/null and b/User-Guide.pdf differ
diff --git a/aclients.c b/aclients.c
new file mode 100755
index 0000000..7f8b722
--- /dev/null
+++ b/aclients.c
@@ -0,0 +1,825 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      aclients.c
+ *
+ * Purpose:   	Multiple concurrent APRS clients for comparing 
+ *		TNC demodulator performance.
+ *		
+ * Description:	Establish connection with multiple servers and 
+ *		compare results side by side.
+ *
+ * Usage:	aclients  8000=AGWPE  8002=DireWolf  COM1=D710A
+ *
+ *		This will connect to multiple physical or virtual
+ *		TNCs, read packets from them, and display results.
+ *
+ *---------------------------------------------------------------*/
+
+
+
+/*
+ * Native Windows:	Use the Winsock interface.
+ * Linux:		Use the BSD socket interface.
+ * Cygwin:		Can use either one.
+ */
+
+
+#if __WIN32__
+
+#include <winsock2.h>
+// default is 0x0400
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501	/* Minimum OS version is XP. */
+#include <ws2tcpip.h>
+#else 
+//#define __USE_XOPEN2KXSI 1
+//#define __USE_XOPEN 1
+#include <stdlib.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/errno.h>
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "version.h"
+
+
+struct agwpe_s {	
+  short portx;			/* 0 for first, 1 for second, etc. */
+  short port_hi_reserved;	
+  short kind_lo;		/* message type */
+  short kind_hi;
+  char call_from[10];
+  char call_to[10];
+  int data_len;			/* Number of data bytes following. */
+  int user_reserved;
+};
+
+
+#if __WIN32__
+static unsigned __stdcall client_thread_net (void *arg);
+static unsigned __stdcall client_thread_serial (void *arg);
+#else
+static void * client_thread_net (void *arg);
+static void * client_thread_serial (void *arg);
+#endif
+
+
+
+/*
+ * Convert Internet address to text.
+ * Can't use InetNtop because it is supported only on Windows Vista and later. 
+ */
+
+static char * ia_to_text (int  Family, void * pAddr, char * pStringBuf, size_t StringBufSize)
+{
+	struct sockaddr_in *sa4;
+	struct sockaddr_in6 *sa6;
+
+	switch (Family) {
+	  case AF_INET:
+	    sa4 = (struct sockaddr_in *)pAddr;
+#if __WIN32__
+	    sprintf (pStringBuf, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1,
+						sa4->sin_addr.S_un.S_un_b.s_b2,
+						sa4->sin_addr.S_un.S_un_b.s_b3,
+						sa4->sin_addr.S_un.S_un_b.s_b4);
+#else
+	    inet_ntop (AF_INET, &(sa4->sin_addr), pStringBuf, StringBufSize);
+#endif
+	    break;
+	  case AF_INET6:
+	    sa6 = (struct sockaddr_in6 *)pAddr;
+#if __WIN32__
+	    sprintf (pStringBuf, "%x:%x:%x:%x:%x:%x:%x:%x",  
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[0]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[3]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[4]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[5]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[6]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[7]));
+#else
+	    inet_ntop (AF_INET6, &(sa6->sin6_addr), pStringBuf, StringBufSize);
+#endif
+	    break;
+	  default:
+	    sprintf (pStringBuf, "Invalid address family!");
+	}
+	assert (strlen(pStringBuf) < StringBufSize);
+	return pStringBuf;
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name: 	main
+ *
+ * Purpose:   	Start up multiple client threads listening to different
+ *		TNCs.   Print packets.  Tally up statistics.
+ *
+ * Usage:	aclients  8000=AGWPE  8002=DireWolf  COM1=D710A
+ *
+ *		Each command line argument is TCP port number or a 
+ *		serial port name.  Follow by = and a text description
+ *		of what is connected.
+ *
+ *		For now, everything is assumed to be on localhost.
+ *		Maybe someday we might recognize host:port=description.
+ *
+ *---------------------------------------------------------------*/
+
+#define MAX_CLIENTS 6
+
+/* Obtained from the command line. */
+
+static int num_clients;
+
+static char hostname[MAX_CLIENTS][50];
+static char port[MAX_CLIENTS][30];
+static char description[MAX_CLIENTS][50];
+
+#if __WIN32__
+	static HANDLE client_th[MAX_CLIENTS];
+#else
+	static pthread_t client_tid[MAX_CLIENTS];
+#endif
+
+#define LINE_WIDTH 120
+static int column_width;
+static char packets[LINE_WIDTH+4];
+static int packet_count[MAX_CLIENTS];
+
+
+//#define PRINT_MINUTES 2
+
+#define PRINT_MINUTES 30
+
+
+
+int main (int argc, char *argv[])
+{
+	int j;
+	time_t start_time, now, next_print_time;
+
+#if __WIN32__
+#else
+	int e;
+
+ 	setlinebuf (stdout);
+#endif
+
+/*
+ * Extract command line args.
+ */
+	num_clients = argc - 1;
+
+	if (num_clients < 1 || num_clients > MAX_CLIENTS) {
+	  printf ("Specify up to %d TNCs on the command line.\n", MAX_CLIENTS);
+	  exit (1);
+	}
+
+	column_width = LINE_WIDTH / num_clients;
+
+	for (j=0; j<num_clients; j++) {
+	  char stemp[100];
+	  char *p;
+	
+	  strcpy (stemp, argv[j+1]);
+	  p = strtok (stemp, "=");
+	  if (p == NULL) {
+	    printf ("Internal error 1\n");
+	    exit (1);
+	  }
+	  strcpy (hostname[j], "localhost");
+	  strcpy (port[j], p);
+	  p = strtok (NULL, "=");
+	  if (p == NULL) {
+	    printf ("Missing description after %s\n", port[j]);
+	    exit (1);
+	  }
+	  strcpy (description[j], p);
+	}
+	
+	//printf ("_WIN32_WINNT = %04x\n", _WIN32_WINNT);
+	//for (j=0; j<num_clients; j++) {
+	//  printf ("%s,%s,%s\n", hostname[j], port[j], description[j]);
+	//}
+
+	memset (packets, ' ', (size_t)LINE_WIDTH);
+	packets[LINE_WIDTH] = '\0';
+
+	for (j=0; j<num_clients; j++) {
+	  packet_count[j] = 0;
+	}
+
+
+	for (j=0; j<num_clients; j++) {
+#if __WIN32__
+	  if (isdigit(port[j][0])) {
+	    client_th[j] = (HANDLE)_beginthreadex (NULL, 0, client_thread_net, (void *)j, 0, NULL);
+	  }
+	  else {
+	    client_th[j] = (HANDLE)_beginthreadex (NULL, 0, client_thread_serial, (void *)j, 0, NULL);
+	  }
+	  if (client_th[j] == NULL) {
+	    printf ("Internal error: Could not create client thread %d.\n", j);
+	    exit (1);
+	  }
+#else
+	  if (isdigit(port[j][0])) {
+	    e = pthread_create (&client_tid[j], NULL, client_thread_net, (void *)(long)j);
+	  }
+	  else {
+	    e = pthread_create (&client_tid[j], NULL, client_thread_serial, (void *)(long)j);
+	  }
+	  if (e != 0) {
+	    perror("Internal error: Could not create client thread.");
+	    exit (1);
+	  }
+#endif
+	}
+
+	start_time = time(NULL);
+	next_print_time = start_time + (PRINT_MINUTES) * 60;
+
+/*
+ * Print results from clients. 
+ */
+	while (1) {
+	  int k;
+	  int something;	
+
+	  SLEEP_MS(100);
+	  
+	  something = 0;
+	  for (k=0; k<LINE_WIDTH && ! something; k++) {
+	    if (packets[k] != ' ') {
+	      something = 1;
+	    }
+	  }
+	  if (something) {
+	    /* time for others to catch up. */
+	    SLEEP_MS(200);
+
+	    printf ("%s\n", packets);
+	    memset (packets, ' ', (size_t)LINE_WIDTH);	
+	  }
+
+	  now = time(NULL);
+	  if (now >= next_print_time) {
+	    next_print_time = now + (PRINT_MINUTES) * 60;
+	
+	    printf ("\nTotals after %d minutes", (int)((now - start_time) / 60));
+
+	    for (j=0; j<num_clients; j++) {
+	      printf (", %s %d", description[j], packet_count[j]);
+	    }
+	    printf ("\n\n");
+	  }
+	}
+
+
+	return 0;  // unreachable
+}
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        client_thread_net
+ *
+ * Purpose:     Establish connection with a TNC via network.
+ *
+ * Inputs:	arg		- My instance index, 0 thru MAX_CLIENTS-1.
+ *
+ * Outputs:	packets		- Received packets are put in the corresponding column.
+ *
+ *--------------------------------------------------------------------*/
+
+#define MAX_HOSTS 30
+
+#if __WIN32__
+static unsigned __stdcall client_thread_net (void *arg)
+#else
+static void * client_thread_net (void *arg)	
+#endif	
+{
+	int my_index;
+	struct addrinfo hints;
+	struct addrinfo *ai_head = NULL;
+	struct addrinfo *ai;
+	struct addrinfo *hosts[MAX_HOSTS];
+	int num_hosts, n;
+	int err;
+	char ipaddr_str[46];		/* text form of IP address */
+#if __WIN32__
+	WSADATA wsadata;
+#endif
+/* 
+ * File descriptor for socket to server. 
+ * Set to -1 if not connected. 
+ * (Don't use SOCKET type because it is unsigned.) 
+*/
+	int server_sock = -1;	
+	struct agwpe_s mon_cmd;
+	char data[1024];
+	int use_chan = -1;
+
+
+	my_index = (int)(long)arg;
+
+#if DEBUGx
+        printf ("DEBUG: client_thread_net %d start, port = '%s'\n", my_index, port[my_index]);
+#endif
+
+#if __WIN32__
+	err = WSAStartup (MAKEWORD(2,2), &wsadata);
+	if (err != 0) {
+	    printf("WSAStartup failed: %d\n", err);
+	    return (0);
+	}
+
+	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
+          printf("Could not find a usable version of Winsock.dll\n");
+          WSACleanup();
+	  //sleep (1);
+          return (0);
+	}
+#endif
+
+	memset (&hints, 0, sizeof(hints));
+
+	hints.ai_family = AF_UNSPEC;	/* Allow either IPv4 or IPv6. */
+	// hints.ai_family = AF_INET;	/* IPv4 only. */
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_protocol = IPPROTO_TCP;
+
+/*
+ * Connect to TNC server.
+ */
+
+	ai_head = NULL;
+	err = getaddrinfo(hostname[my_index], port[my_index], &hints, &ai_head);
+	if (err != 0) {
+#if __WIN32__
+	  printf ("Can't get address for server %s, err=%d\n", 
+					hostname[my_index], WSAGetLastError());
+#else 
+	  printf ("Can't get address for server %s, %s\n", 
+					hostname[my_index], gai_strerror(err));
+#endif
+	  freeaddrinfo(ai_head);
+      	  exit (1);
+	}
+
+#if DEBUG_DNS
+	printf ("getaddrinfo returns:\n");
+#endif
+	num_hosts = 0;
+	for (ai = ai_head; ai != NULL; ai = ai->ai_next) {
+#if DEBUG_DNS
+	  ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, sizeof(ipaddr_str));
+	  printf ("    %s\n", ipaddr_str);
+#endif
+	  hosts[num_hosts] = ai;
+	  if (num_hosts < MAX_HOSTS) num_hosts++;
+	}
+
+#if DEBUG_DNS
+	printf ("addresses for hostname:\n");
+	for (n=0; n<num_hosts; n++) {
+	  ia_to_text (hosts[n]->ai_family, hosts[n]->ai_addr, ipaddr_str, sizeof(ipaddr_str));
+	  printf ("    %s\n", ipaddr_str);
+	}
+#endif
+
+	// Try each address until we find one that is successful.
+
+	for (n=0; n<num_hosts; n++) {
+	  int is;
+
+	  ai = hosts[n];
+
+	  ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, sizeof(ipaddr_str));
+	  is = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+#if __WIN32__
+	  if (is == INVALID_SOCKET) {
+	    printf ("Socket creation failed, err=%d", WSAGetLastError());
+	    WSACleanup();
+	    is = -1;
+	    continue;
+	  }
+#else
+	  if (err != 0) {
+	    printf ("Socket creation failed, err=%s", gai_strerror(err));
+	    (void) close (is);
+	    is = -1;
+	    continue;
+	  }
+#endif
+
+#ifndef DEBUG_DNS 
+	  err = connect(is, ai->ai_addr, (int)ai->ai_addrlen);
+#if __WIN32__
+	  if (err == SOCKET_ERROR) {
+#if DEBUGx
+	    printf("Connect to %s on %s (%s), port %s failed.\n",
+					description[my_index], hostname[my_index], ipaddr_str, port[my_index]);
+#endif
+	    closesocket (is);
+	    is = -1;
+	    continue;
+	  }
+#else
+	  if (err != 0) {
+#if DEBUGx
+	    printf("Connect to %s on %s (%s), port %s failed.\n",
+					description[my_index], hostname[my_index], ipaddr_str, port[my_index]);
+#endif
+	    (void) close (is);
+	    is = -1;
+	    continue;
+	  }
+	  int flag = 1;
+	  err = setsockopt (is, IPPROTO_TCP, TCP_NODELAY, (void*)(long)(&flag), (socklen_t)sizeof(flag));
+	  if (err < 0) {
+	    printf("setsockopt TCP_NODELAY failed.\n");
+	  }
+#endif
+
+/* Success. */
+
+ 	  printf("Client %d now connected to %s on %s (%s), port %s\n", 
+			my_index, description[my_index], hostname[my_index], ipaddr_str, port[my_index] );
+
+	  server_sock = is;
+#endif	  
+	  break;
+	}
+
+	freeaddrinfo(ai_head);
+
+	if (server_sock == -1) {
+
+ 	  printf("Client %d unable to connect to %s on %s (%s), port %s\n", 
+			my_index, description[my_index], hostname[my_index], ipaddr_str, port[my_index] );
+	  exit (1);
+	}
+
+/*
+ * Send command to toggle reception of frames in raw format.
+ *
+ * Note: Monitor format is only for UI frames.
+ * It also discards the via path.
+ */
+
+	memset (&mon_cmd, 0, sizeof(mon_cmd));
+
+	mon_cmd.kind_lo = 'k';
+
+#if __WIN32__	      
+	send (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd), 0);
+#else
+	(void)write (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
+#endif
+
+
+/*
+ * Print all of the monitored packets.
+ */
+
+	while (1) {
+	  int n;
+
+#if __WIN32__
+	  n = recv (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd), 0);
+#else
+	  n = read (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
+#endif
+
+	  if (n != sizeof(mon_cmd)) {
+	    printf ("Read error, client %d received %d command bytes.\n", my_index, n);
+	    exit (1);
+	  }
+
+#if DEBUGx
+	  printf ("client %d received '%c' data, data_len = %d\n", 
+			my_index, mon_cmd.kind_lo, mon_cmd.data_len);
+#endif
+	  assert (mon_cmd.data_len >= 0 && mon_cmd.data_len < sizeof(data));
+
+	  if (mon_cmd.data_len > 0) {
+#if __WIN32__
+	    n = recv (server_sock, data, mon_cmd.data_len, 0);
+#else
+	    n = read (server_sock, data, mon_cmd.data_len);
+#endif
+
+	    if (n != mon_cmd.data_len) {
+	      printf ("Read error, client %d received %d data bytes.\n", my_index, n);
+	      exit (1);
+	    }
+	  }
+
+/* 
+ * Print it and add to counter.
+ * The AGWPE score was coming out double the proper value because 
+ * we were getting the same thing from ports 2 and 3.
+ * 'use_chan' is the first channel we hear from.
+ * Listen only to that one.
+ */
+
+	  if (mon_cmd.kind_lo == 'K' && (use_chan == -1 || use_chan == mon_cmd.portx)) {
+	    packet_t pp;
+	    char *pinfo;
+	    int info_len;
+	    char result[400];
+	    char *p;
+	    int col, len;
+
+	    //printf ("server %d, portx = %d\n", my_index, mon_cmd.portx);
+
+	    use_chan == mon_cmd.portx;
+	    pp = ax25_from_frame ((unsigned char *)(data+1), mon_cmd.data_len-1, -1);
+	    ax25_format_addrs (pp, result);
+	    info_len = ax25_get_info (pp, (unsigned char **)(&pinfo));
+	    pinfo[info_len] = '\0';
+	    strcat (result, pinfo);
+	    for (p=result; *p!='\0'; p++) {
+	      if (! isprint(*p)) *p = ' ';
+	    }
+#if DEBUGx
+	    printf ("[%d] %s\n", my_index, result);
+#endif
+	    col = column_width * my_index;
+	    len = strlen(result);
+#define MARGIN 3
+	    if (len > column_width - 3) {
+	      len = column_width - 3;
+	    }
+	    if (packets[col] == ' ') {
+	      memcpy (packets+col, result, (size_t)len);
+	    }
+	    else {
+	      memcpy (packets+col, "OVERRUN!    ", (size_t)10);
+	    }
+	    	    
+	    ax25_delete (pp);
+	    packet_count[my_index]++;
+	  }
+	}
+
+} /* end client_thread_net */
+
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        client_thread_serial
+ *
+ * Purpose:     Establish connection with a TNC via serial port.
+ *
+ * Inputs:	arg		- My instance index, 0 thru MAX_CLIENTS-1.
+ *
+ * Outputs:	packets		- Received packets are put in the corresponding column.
+ *
+ *--------------------------------------------------------------------*/
+
+#if __WIN32__
+typedef HANDLE MYFDTYPE;
+#define MYFDERROR INVALID_HANDLE_VALUE
+#else
+typedef int MYFDTYPE;
+#define MYFDERROR (-1)
+#endif
+
+
+#if __WIN32__
+static unsigned __stdcall client_thread_serial (void *arg)
+#else
+static void * client_thread_serial (void *arg)	
+#endif	
+{
+	int my_index = (int)(long)arg;
+
+#if __WIN32__
+
+	MYFDTYPE fd;
+	DCB dcb;
+	int ok;
+
+
+	fd = CreateFile(port[my_index], GENERIC_READ | GENERIC_WRITE, 
+			0, NULL, OPEN_EXISTING, 0, NULL);
+
+	if (fd == MYFDERROR) {
+ 	  printf("Client %d unable to connect to %s on %s.\n", 
+			my_index, description[my_index], port[my_index] );
+	  exit (1);
+	}
+
+	/* Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363201(v=vs.85).aspx */
+
+	memset (&dcb, 0, sizeof(dcb));
+	dcb.DCBlength = sizeof(DCB);
+
+	ok = GetCommState (fd, &dcb);
+	if (! ok) {
+	  printf ("GetCommState failed.\n");
+	}
+
+	/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */
+
+	dcb.BaudRate = 9600;
+	dcb.fBinary = 1;
+	dcb.fParity = 0;
+	dcb.fOutxCtsFlow = 0;
+	dcb.fOutxDsrFlow = 0;
+	dcb.fDtrControl = 0;
+	dcb.fDsrSensitivity = 0;
+	dcb.fOutX = 0;
+	dcb.fInX = 0;
+	dcb.fErrorChar = 0;
+	dcb.fNull = 0;		/* Don't drop nul characters! */
+	dcb.fRtsControl = 0;
+	dcb.ByteSize = 8;
+	dcb.Parity = NOPARITY;
+	dcb.StopBits = ONESTOPBIT;
+
+	ok = SetCommState (fd, &dcb);
+	if (! ok) {
+	  printf ("SetCommState failed.\n");
+	}
+
+#else
+
+/* Linux version. */
+
+	int fd;
+	struct termios ts;
+	int e;
+
+	fd = open (port[my_index], O_RDWR);
+
+	if (fd == MYFDERROR) {
+ 	  printf("Client %d unable to connect to %s on %s.\n", 
+			my_index, description[my_index], port[my_index] );
+	  exit (1);
+	}
+
+	e = tcgetattr (fd, &ts);
+	if (e != 0) { perror ("nm tcgetattr"); }
+
+	cfmakeraw (&ts);
+	
+	// TODO: speed?
+	ts.c_cc[VMIN] = 1;	/* wait for at least one character */
+	ts.c_cc[VTIME] = 0;	/* no fancy timing. */
+
+	e = tcsetattr (fd, TCSANOW, &ts);
+	if (e != 0) { perror ("nm tcsetattr"); }
+#endif
+
+
+/* Success. */
+
+ 	printf("Client %d now connected to %s on %s\n", 
+			my_index, description[my_index], port[my_index] );
+
+/*
+ * Assume we are already in monitor mode.
+ */
+
+
+/*
+ * Print all of the monitored packets.
+ */
+
+	while (1) {
+	  unsigned char ch;
+	  char result[500];
+	  int col, len;
+	  int done;
+	  char *p;
+
+	  len = 0;
+	  done = 0;
+
+	  while ( ! done) {
+
+#if __WIN32__
+	    DWORD n;
+
+	    if (! ReadFile (fd, &ch, 1, &n, NULL)) {
+	      printf ("Read error on %s.\n", description[my_index]);
+	      CloseHandle (fd);
+	      exit (1);
+	    }
+	  
+#else
+	    int n;
+
+	    if ( ( n = read(fd, & ch, 1)) < 0) {
+	      printf ("Read error on %s.\n", description[my_index]);
+	      close (fd);
+	      exit (1);
+	    }
+#endif
+	    if (n == 1) {
+
+/*
+ * Try to build one line for each packet.
+ * The KPC3+ breaks a packet into two lines like this:
+ *
+ *	KB1ZXL-1>T2QY5P,W1MHL*,WIDE2-1: <<UI>>:
+ *	`c0+!h4>/]"4a}146.520MHz Listening, V-Alert & WLNK-1=
+ *
+ *	N8VIM>BEACON,W1XM,WB2OSZ-1,WIDE2*: <UI>:
+ * 	!4240.85N/07133.99W_PHG72604/ Pepperell, MA. WX. 442.9+ PL100
+ *
+ * Don't know why some are <<UI>> and some <UI>.
+ *
+ * Anyhow, ignore the return character if preceded by >:
+ */
+	      if (ch == '\r') { 
+	        if (len >= 10 && result[len-2] == '>' && result[len-1] == ':') {
+	          continue;
+	        }
+	        done = 1; 
+	        continue; 
+	      }
+	      if (ch == '\n') continue;
+	      result[len++] = ch;
+	    }
+	  }
+	  result[len] = '\0';
+
+/* 
+ * Print it and add to counter.
+ */
+	 if (len > 0) {
+	  /* Blank any unprintable characters. */
+	  for (p=result; *p!='\0'; p++) {
+	    if (! isprint(*p)) *p = ' ';
+	  }
+#if DEBUGx
+	  printf ("[%d] %s\n", my_index, result);
+#endif
+	  col = column_width * my_index;
+	  if (len > column_width - 3) {
+	    len = column_width - 3;
+	  }
+	  if (packets[col] == ' ') {
+	    memcpy (packets+col, result, (size_t)len);
+	  }
+	  else {
+	    memcpy (packets+col, "OVERRUN!    ", (size_t)10);
+	  }
+	  packet_count[my_index]++;
+         }
+	}
+
+} /* end client_thread_serial */
+
+/* end aclients.c */
diff --git a/aprs_tt.c b/aprs_tt.c
new file mode 100755
index 0000000..1b4a791
--- /dev/null
+++ b/aprs_tt.c
@@ -0,0 +1,1436 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013,2014  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+/*------------------------------------------------------------------
+ *
+ * Module:      aprs_tt.c
+ *
+ * Purpose:   	APRStt gateway.
+ *		
+ * Description: Transfer touch tone messages into the APRS network.
+ *
+ * References:	This is based upon APRStt (TM) documents with some
+ *		artistic freedom.
+ *
+ *		http://www.aprs.org/aprstt.html
+ *
+ *---------------------------------------------------------------*/
+
+// TODO:  clean up terminolgy.  
+// "Message" has a specific meaning in APRS and this is not it.  
+// Touch Tone sequence might be appropriate.
+// What do we call the parts separated by * key?   Entry?  Field?
+
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+
+#include <ctype.h>
+#include <assert.h>
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "hdlc_rec2.h"		/* for process_rec_frame */
+#include "textcolor.h"
+#include "aprs_tt.h"
+#include "tt_text.h"
+#include "tt_user.h"
+#include "symbols.h"
+#include "latlong.h"
+
+
+#if __WIN32__
+char *strtok_r(char *str, const char *delim, char **saveptr);
+#endif
+
+#include "utm/LatLong-UTMconversion.h"
+
+
+//TODO: #include "tt_user.h"
+
+
+
+/*
+ * Touch Tone sequences are accumulated here until # terminator found.
+ * Kept separate for each audio channel.
+ */
+
+#define MAX_MSG_LEN 100
+
+static char msg_str[MAX_CHANS][MAX_MSG_LEN+1];
+static int msg_len[MAX_CHANS];
+
+static void aprs_tt_message (int chan, char *msg);
+static int parse_fields (char *msg);
+static int parse_callsign (char *e);
+static int parse_object_name (char *e);
+static int parse_symbol (char *e);
+static int parse_location (char *e);
+static int parse_comment (char *e);
+static int expand_macro (char *e);
+static void raw_tt_data_to_app (int chan, char *msg);
+static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *bstr, char *dstr);
+
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        aprs_tt_init
+ *
+ * Purpose:     Initialize the APRStt gateway at system startup time.
+ *
+ * Inputs:      Configuration options gathered by config.c.
+ *
+ * Global out:	Make our own local copy of the structure here.
+ *
+ * Returns:     None
+ *
+ * Description:	The main program needs to call this at application
+ *		start up time after reading the configuration file.
+ *
+ *		TT_MAIN is defined for unit testing.
+ *
+ *----------------------------------------------------------------*/
+
+static struct tt_config_s tt_config;
+
+#if TT_MAIN
+#define NUM_TEST_CONFIG (sizeof(test_config) / sizeof (struct ttloc_s))
+static struct ttloc_s test_config[] = {
+
+	{ TTLOC_POINT, "B01", .point.lat = 12.25, .point.lon = 56.25 },
+	{ TTLOC_POINT, "B988", .point.lat = 12.50, .point.lon = 56.50 },
+
+	{ TTLOC_VECTOR, "B5bbbdddd", .vector.lat = 53., .vector.lon = -1., .vector.scale = 1000. },  /* km units */
+
+	/* Hilltop Tower http://www.aprs.org/aprs-jamboree-2013.html */
+	{ TTLOC_VECTOR, "B5bbbddd", .vector.lat = 37+55.37/60., .vector.lon = -(81+7.86/60.), .vector.scale = 16.09344 },   /* .01 mile units */
+
+	{ TTLOC_GRID, "B2xxyy", .grid.lat0 = 12.00, .grid.lon0 = 56.00, 
+				.grid.lat9 = 12.99, .grid.lon9 = 56.99 },
+	{ TTLOC_GRID, "Byyyxxx", .grid.lat0 = 37 + 50./60.0, .grid.lon0 = 81, 
+				.grid.lat9 = 37 + 59.99/60.0, .grid.lon9 = 81 + 9.99/60.0 },
+
+	{ TTLOC_MACRO, "xxyyy", .macro.definition = "B9xx*AB166*AA2B4C5B3B0Ayyy" },
+}; 
+#endif
+
+
+void aprs_tt_init (struct tt_config_s *p)
+{
+	int c;
+
+#if TT_MAIN
+	/* For unit testing. */
+
+	memset (&tt_config, 0, sizeof(struct tt_config_s));	
+	tt_config.ttloc_size = NUM_TEST_CONFIG;
+	tt_config.ttloc_ptr = test_config;
+	tt_config.ttloc_len = NUM_TEST_CONFIG;
+
+	/* Don't care about xmit timing or corral here. */
+#else
+	memcpy (&tt_config, p, sizeof(struct tt_config_s));
+#endif
+	for (c=0; c<MAX_CHANS; c++) {	
+	  msg_len[c] = 0;
+	  msg_str[c][0] = '\0';
+	}
+
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        aprs_tt_button
+ *
+ * Purpose:     Process one received button press.
+ *
+ * Inputs:      chan		- Audio channel it came from.
+ *
+ *		button		0123456789ABCD*#	- Received button press.
+ *				$			- No activity timeout.
+ *				space			- Quiet time filler.
+ *
+ * Returns:     None
+ *
+ * Description:	Individual key presses are accumulated here until
+ *		the # message terminator is found.
+ *		The complete message is then processed.
+ *		The touch tone decoder sends $ if no activity
+ *		for some amount of time, perhaps 5 seconds.
+ *		A partially accumulated messge is discarded if
+ *		there is a long gap.
+ *
+ *		'.' means no activity during processing period.
+ *		space, between blocks, shouldn't get here.
+ *
+ *----------------------------------------------------------------*/
+
+#ifndef TT_MAIN
+
+void aprs_tt_button (int chan, char button)
+{
+	static int poll_period = 0;
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+
+
+// TODO:  Might make more sense to put timeout here rather in the dtmf decoder.
+
+	if (button == '$') {
+
+/* Timeout reset. */
+
+	  msg_len[chan] = 0;
+	  msg_str[chan][0] = '\0';
+	}
+	else if (button != '.' && button != ' ') {
+	  if (msg_len[chan] < MAX_MSG_LEN) {
+	    msg_str[chan][msg_len[chan]++] = button;
+	    msg_str[chan][msg_len[chan]] = '\0';
+	  }
+	  if (button == '#') {
+
+/* Process complete message. */
+	
+	    aprs_tt_message (chan, msg_str[chan]);
+	    msg_len[chan] = 0;
+	    msg_str[chan][0] = '\0';
+	  }
+	}
+	else {
+
+/* Idle time. Poll occasionally for processing. */
+	  
+	  poll_period++;
+	  if (poll_period >= 39) {
+	    poll_period = 0;
+	    tt_user_background ();
+	  }
+	}	
+  
+} /* end aprs_tt_button */
+
+#endif
+
+/*------------------------------------------------------------------
+ *
+ * Name:        aprs_tt_message
+ *
+ * Purpose:     Process complete received touch tone sequence
+ *		terminated by #.
+ *
+ * Inputs:      chan		- Audio channel it came from.
+ *
+ *		msg		- String of DTMF buttons.
+ *				  # should be the final character.
+ *
+ * Returns:     None
+ *
+ * Description:	Process a complete message.
+ *		It should have one or more fields separatedy by *
+ *		and terminated by a final # like these:
+ *
+ *		callsign #
+ *		entry1 * callsign #
+ *		entry1 * entry * callsign #
+ *
+ *----------------------------------------------------------------*/
+
+static char m_callsign[20];	/* really object name */
+
+/*
+ * Standard APRStt has symbol code 'A' (box) with overlay of 0-9, A-Z. 
+ *
+ * Dire Wolf extension allows:
+ *	Symbol table '/' (primary), any symbol code.
+ *	Symbol table '\' (alternate), any symbol code.
+ *	Alternate table symbol code, overlay of 0-9, A-Z.
+ */
+static char m_symtab_or_overlay;
+static char m_symbol_code;
+
+static double m_longitude;
+static double m_latitude;
+static char m_comment[200];
+static char m_freq[12];
+static char m_mic_e;
+static char m_dao[6];
+static int m_ssid;
+
+//#define G_UNKNOWN -999999
+
+
+
+void aprs_tt_message (int chan, char *msg)
+{
+	int err;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("\n\"%s\"\n", msg);
+#endif
+
+/* 
+ * Discard empty message. 
+ * In case # is there as optional start. 
+ */
+
+	if (msg[0] == '#') return;
+
+/*
+ * This takes the place of the usual line with audio level.
+ * Do it here, rather than in process_rec_frame, so any
+ * error messages are associated with the DTMF message
+ * rather than the most recent regular AX.25 frame.
+ */
+
+#ifndef TT_MAIN
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("\nDTMF message\n");
+#endif
+
+/*
+ * The parse functions will fill these in. 
+ */
+	strcpy (m_callsign, "");
+	m_symtab_or_overlay = '\\';
+	m_symbol_code = 'A';
+	m_longitude = G_UNKNOWN; 
+	m_latitude = G_UNKNOWN; 
+	strcpy (m_comment, "");
+	strcpy (m_freq, "");
+	m_mic_e = ' ';
+	strcpy (m_dao, "!T  !");	/* start out unknown */
+	m_ssid = 12;
+
+/* 
+ * Send raw touch tone data to application. 
+ */
+	raw_tt_data_to_app (chan, msg);
+
+/*
+ * Parse the touch tone sequence.
+ */
+	err = parse_fields (msg);
+
+#if defined(DEBUG) || defined(TT_MAIN)
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("callsign=\"%s\", ssid=%d, symbol=\"%c%c\", freq=\"%s\", comment=\"%s\", lat=%.4f, lon=%.4f, dao=\"%s\"\n", 
+		m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_freq, m_comment, m_latitude, m_longitude, m_dao);
+#endif
+
+
+	if (err == 0) {
+
+/*
+ * Digested successfully.  Add to our list of users and schedule transmissions.
+ */
+
+#ifndef TT_MAIN
+	  err = tt_user_heard (m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_latitude, m_longitude,
+		m_freq, m_comment, m_mic_e, m_dao);
+#endif
+	}
+
+	// TODO send response to user.
+	// err == 0 OK, others, suitable error response.
+
+
+} /* end aprs_tt_message */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        parse_fields
+ *
+ * Purpose:     Separate the complete string of touch tone characters
+ *		into fields, delimited by *, and process each.
+ *
+ * Inputs:      msg		- String of DTMF buttons.
+ *
+ * Returns:     None
+ *
+ * Description:	It should have one or more fields separatedy by *.
+ *
+ *		callsign #
+ *		entry1 * callsign #
+ *		entry1 * entry * callsign #
+ *
+ *		Note that this will be used recursively when macros
+ *		are expanded.
+ *
+ *		"To iterate is human, to recurse divine."
+ *
+ * Returns:	0 for success or one of the TT_ERROR_... codes.
+ *
+ *----------------------------------------------------------------*/
+
+static int parse_fields (char *msg)
+{
+	char stemp[MAX_MSG_LEN+1];
+	char *e;
+	char *save;
+
+	strcpy (stemp, msg);
+	e = strtok_r (stemp, "*#", &save);
+	while (e != NULL) {
+
+	  switch (*e) {
+
+	    case 'A': 
+	      
+	      switch (e[1]) {
+	        case 'A':
+	          parse_object_name (e);
+	          break;
+	        case 'B':
+	          parse_symbol (e);
+	          break;
+	        default:
+	          parse_callsign (e);
+	          break;
+	      }
+	      break;
+
+	    case 'B': 
+	      parse_location (e);
+	      break;
+
+	    case 'C': 
+	      parse_comment (e);
+	      break;
+
+	    case '0': 
+	    case '1': 
+	    case '2': 
+	    case '3': 
+	    case '4': 
+	    case '5': 
+	    case '6': 
+	    case '7': 
+	    case '8': 
+	    case '9': 
+	      expand_macro (e);
+	      break;
+
+	    case '\0':
+	      /* Empty field.  Just ignore it. */
+	      /* This would happen if someone uses a leading *. */
+	      break;
+
+	    default:
+
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Entry does not start with A, B, C, or digit: \"%s\"\n", msg);
+	      return (TT_ERROR_D_MSG);
+
+	  }
+	
+	  e = strtok_r (NULL, "*#", &save);
+	}
+
+	return (0);
+
+} /* end parse_fields */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        expand_macro
+ *
+ * Purpose:     Expand compact form "macro" to full format then process.
+ *
+ * Inputs:      e		- An "entry" extracted from a complete
+ *				  APRStt messsage.
+ *				  In this case, it should contain only digits.
+ *
+ * Returns:	0 for success or one of the TT_ERROR_... codes.
+ *
+ * Description:	Separate out the fields, perform substitution, 
+ *		call parse_fields for processing.
+ *
+ *----------------------------------------------------------------*/
+
+static int expand_macro (char *e) 
+{
+	int len;
+	int ipat;
+	char xstr[20], ystr[20], zstr[20], bstr[20], dstr[20];
+	char stemp[MAX_MSG_LEN+1];
+	char *d, *s;
+
+
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("Macro tone sequence: '%s'\n", e);
+
+	len = strlen(e);
+
+	ipat = find_ttloc_match (e, xstr, ystr, zstr, bstr, dstr);
+
+	if (ipat >= 0) {
+
+	  dw_printf ("Matched pattern %3d: '%s', x=%s, y=%s, z=%s, b=%s, d=%s\n", ipat, tt_config.ttloc_ptr[ipat].pattern, xstr, ystr, zstr, bstr, dstr);
+
+	  dw_printf ("Replace with:        '%s'\n", tt_config.ttloc_ptr[ipat].macro.definition);
+
+	  if (tt_config.ttloc_ptr[ipat].type != TTLOC_MACRO) {
+
+	     /* Found match to a different type.  Really shouldn't be here. */
+	     /* Print internal error message... */
+	     dw_printf ("expand_macro: type != TTLOC_MACRO\n");
+	     return (TT_ERROR_INTERNAL);
+	  }
+
+/*
+ * We found a match for the length and any fixed digits.
+ * Substitute values in to the definition.
+ */		
+	  
+	  strcpy (stemp, "");
+
+	  for (d = tt_config.ttloc_ptr[ipat].macro.definition; *d != '\0'; d++) {
+
+	    while (( *d == 'x' || *d == 'y' || *d == 'z') && *d == d[1]) {
+	      /* Collapse adjacent matching substitution characters. */
+	      d++;
+	    }
+
+	    switch (*d) {
+	      case 'x':
+		strcat (stemp, xstr);
+	        break;
+	      case 'y':
+		strcat (stemp, ystr);
+	        break;
+	      case 'z':
+		strcat (stemp, zstr);
+	        break;
+	      default:
+		{
+	        char c1[2];
+	        c1[0] = *d;
+	        c1[1] = '\0';
+	        strcat (stemp, c1); 
+		}
+		break;
+	    }
+	  }
+/*
+ * Process as if we heard this over the air.
+ */
+
+	  dw_printf ("After substitution:  '%s'\n", stemp);
+	  return (parse_fields (stemp));
+	}
+	else {
+	  /* Send reject sound. */
+	  /* Does not match any macro definitions. */
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Tone sequence did not match any pattern\n");
+	  return (TT_ERROR_MACRO_NOMATCH);
+	}
+	
+	/* should be unreachable */
+	return (0);
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        parse_callsign
+ *
+ * Purpose:     Extract callsign or object name from touch tone message. 
+ *
+ * Inputs:      e		- An "entry" extracted from a complete
+ *				  APRStt messsage.
+ *				  In this case, it should start with "A".
+ *
+ * Outputs:	m_callsign
+ *
+ *		m_symtab_or_overlay - Set to 0-9 or A-Z if specified.
+ *
+ *		m_symbol_code	- Always set to 'A'.
+ *
+ * Returns:	0 for success or one of the TT_ERROR_... codes.
+ *
+ * Description:	We recognize 3 different formats:
+ *
+ *		Annn		- 3 digits are a tactical callsign.  No overlay.
+ *
+ *		Annnvk		- Abbreviation with 3 digits, numeric overlay, checksum.
+ *		Annnvvk		- Abbreviation with 3 digits, letter overlay, checksum.
+ *
+ *		Att...ttvk	- Full callsign in two key method, numeric overlay, checksum.
+ *		Att...ttvvk	- Full callsign in two key method, letter overlay, checksum.
+ *
+ *----------------------------------------------------------------*/
+
+static int checksum_not_ok (char *str, int len, char found)
+{
+	int i;
+	int sum;
+	char expected;
+
+	sum = 0;
+	for (i=0; i<len; i++) {
+	  if (isdigit(str[i])) {
+	    sum += str[i] - '0';
+	  }
+	  else if (str[i] >= 'A' && str[i] <= 'D') {
+	    sum += str[i] - 'A' + 10;
+	  }
+	  else {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("aprs_tt: checksum: bad character \"%c\" in checksum calculation!\n", str[i]);
+	  }
+	}
+	expected =  '0' + (sum % 10);
+
+	if (expected != found) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Bad checksum for \"%.*s\".  Expected %c but received %c.\n", len, str, expected, found);
+	    return (TT_ERROR_BAD_CHECKSUM);
+	}
+	return (0);
+}
+
+
+static int parse_callsign (char *e)
+{
+	int len;
+	int c_length;
+	char tttemp[40], stemp[30];
+
+	assert (*e == 'A');
+
+	len = strlen(e);
+
+/*
+ * special case: 3 digit tactical call.
+ */
+
+	if (len == 4 && isdigit(e[1]) && isdigit(e[2]) && isdigit(e[3])) {
+	  strcpy (m_callsign, e+1);
+	  return (0);
+	}
+
+/* 
+ * 3 digit abbreviation:  We only do the parsing here.
+ * Another part of application will try to find corresponding full call.
+ */
+
+	if ((len == 6 && isdigit(e[1]) && isdigit(e[2]) && isdigit(e[3]) && isdigit(e[4]) && isdigit(e[5])) ||
+	    (len == 7 && isdigit(e[1]) && isdigit(e[2]) && isdigit(e[3]) && isdigit(e[4]) && isupper(e[5]) && isdigit(e[6]))) {
+
+	  int cs_err = checksum_not_ok (e+1, len-2, e[len-1]);
+
+	  if (cs_err != 0) {
+	    return (cs_err);
+	  }
+
+	  strncpy (m_callsign, e+1, 3);
+	  m_callsign[3] = '\0';
+	
+	  if (len == 7) {
+	    tttemp[0] = e[len-3];
+	    tttemp[1] = e[len-2];
+	    tttemp[2] = '\0';
+	    tt_two_key_to_text (tttemp, 0, stemp);
+	    m_symbol_code = 'A';
+	    m_symtab_or_overlay = stemp[0];
+	  }
+	  else {
+	    m_symbol_code = 'A';
+	    m_symtab_or_overlay = e[len-2];
+	  }
+	  return (0);
+	}
+
+/* 
+ * Callsign in two key format.
+ */
+
+	if (len >= 7 && len <= 24) {
+
+	  int cs_err = checksum_not_ok (e+1, len-2, e[len-1]);
+
+	  if (cs_err != 0) {
+	    return (cs_err);
+	  }
+
+	
+	  if (isupper(e[len-2])) {
+	    strncpy (tttemp, e+1, len-4);
+	    tttemp[len-4] = '\0';
+	    tt_two_key_to_text (tttemp, 0, m_callsign);
+
+	    tttemp[0] = e[len-3];
+	    tttemp[1] = e[len-2];
+	    tttemp[2] = '\0';
+	    tt_two_key_to_text (tttemp, 0, stemp);
+	    m_symbol_code = 'A';
+	    m_symtab_or_overlay = stemp[0];
+	  }
+	  else {
+	    strncpy (tttemp, e+1, len-3);
+	    tttemp[len-3] = '\0';
+	    tt_two_key_to_text (tttemp, 0, m_callsign);
+
+	    m_symbol_code = 'A';
+	    m_symtab_or_overlay = e[len-2];
+	  }
+	  return (0);
+	}
+
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf ("Touch tone callsign not valid: \"%s\"\n", e);
+	return (TT_ERROR_INVALID_CALL);
+}
+
+/*------------------------------------------------------------------
+ *
+ * Name:        parse_object_name 
+ *
+ * Purpose:     Extract object name from touch tone message. 
+ *
+ * Inputs:      e		- An "entry" extracted from a complete
+ *				  APRStt messsage.
+ *				  In this case, it should start with "AA".
+ *
+ * Outputs:	m_callsign
+ *
+ *		m_ssid		- Cleared to remove the default of 12.
+ *
+ * Returns:	0 for success or one of the TT_ERROR_... codes.
+ *
+ * Description:	Data format
+ *
+ *		AAtt...tt	- Symbol name, two key method, up to 9 characters.
+ *
+ *----------------------------------------------------------------*/
+
+
+static int parse_object_name (char *e)
+{
+	int len;
+	int c_length;
+	char tttemp[40], stemp[30];
+
+	assert (e[0] == 'A');
+	assert (e[1] == 'A');
+
+	len = strlen(e);
+
+/* 
+ * Object name in two key format.
+ */
+
+	if (len >= 2 + 1 && len <= 30) {
+
+	  if (tt_two_key_to_text (e+2, 0, m_callsign) == 0) {
+	    m_callsign[9] = '\0';  /* truncate to 9 */
+	    m_ssid = 0;		/* No ssid for object name */
+	    return (0);
+	  }
+	}
+
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf ("Touch tone object name not valid: \"%s\"\n", e);
+
+	return (TT_ERROR_INVALID_OBJNAME);
+
+}  /* end parse_oject_name */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        parse_symbol 
+ *
+ * Purpose:     Extract symbol from touch tone message. 
+ *
+ * Inputs:      e		- An "entry" extracted from a complete
+ *				  APRStt messsage.
+ *				  In this case, it should start with "AB".
+ *
+ * Outputs:	m_symtab_or_overlay
+ *
+ * 		m_symbol_code
+ *
+ * Returns:	0 for success or one of the TT_ERROR_... codes.
+ *
+ * Description:	Data format
+ *
+ *		AB1nn		- Symbol from primary symbol table.  
+ *				  Two digits nn are the same as in the GPSCnn 
+ *				  generic address used as a destination. 
+ *
+ *		AB2nn		- Symbol from alternate symbol table.  
+ *				  Two digits nn are the same as in the GPSEnn 
+ *				  generic address used as a destination. 
+ *
+ *		AB0nnvv		- Symbol from alternate symbol table.  
+ *				  Two digits nn are the same as in the GPSEnn 
+ *				  generic address used as a destination.
+ *	 			  vv is an overlay digit or letter in two key method.
+ *
+ *----------------------------------------------------------------*/
+
+
+static int parse_symbol (char *e)
+{
+	int len;
+	char nstr[4];
+	int nn;
+	char stemp[10];
+
+	assert (e[0] == 'A');
+	assert (e[1] == 'B');
+
+	len = strlen(e);
+
+	if (len >= 4 && len <= 10) {
+
+	  nstr[0] = e[3];
+	  nstr[1] = e[4];
+	  nstr[2] = '\0';
+
+	  nn = atoi (nstr);
+	  if (nn < 1) {
+	    nn = 1;
+	  }
+	  else if (nn > 94) {
+	    nn = 94;
+	  }
+
+	  switch (e[2]) {
+
+	    case '1':
+	      m_symtab_or_overlay = '/';
+	      m_symbol_code = 32 + nn;
+	      return (0);
+	      break;
+
+	    case '2':
+	      m_symtab_or_overlay = '\\';
+	      m_symbol_code = 32 + nn;
+	      return (0);
+	      break;
+
+	    case '0':
+	      if (len >= 6) {
+	        if (tt_two_key_to_text (e+5, 0, stemp) == 0) {
+	          m_symbol_code = 32 + nn;
+	          m_symtab_or_overlay = stemp[0];
+	          return (0);
+	        }
+	      }
+	      break;
+	  }
+	}
+
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf ("Touch tone symbol not valid: \"%s\"\n", e);
+
+	return (TT_ERROR_INVALID_SYMBOL);
+
+}  /* end parse_oject_name */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        parse_location
+ *
+ * Purpose:     Extract location from touch tone message. 
+ *
+ * Inputs:      e		- An "entry" extracted from a complete
+ *				  APRStt messsage.
+ *				  In this case, it should start with "B".
+ *
+ * Outputs:	m_latitude
+ *		m_longitude
+ *
+ * Returns:	0 for success or one of the TT_ERROR_... codes.
+ *
+ * Description:	There are many different formats recognizable
+ *		by total number of digits and sometimes the first digit.
+ *
+ *		We handle most of them in a general way, processing
+ *		them in 4 groups:
+ *
+ *		* points
+ *		* vector
+ *		* grid
+ *		* utm
+ *
+ *----------------------------------------------------------------*/
+
+
+
+/* Average radius of earth in meters. */
+#define R 6371000.
+
+/* Convert between degrees and radians. */
+
+#define D2R(a) ((a) * 2. * M_PI / 360.)
+#define R2D(a) ((a) * 360. / (2*M_PI))
+
+
+static int parse_location (char *e)
+{
+	int len;
+	int ipat;
+	char xstr[20], ystr[20], zstr[20], bstr[20], dstr[20];
+	double x, y, dist, bearing;
+	double lat0, lon0;
+	double lat9, lon9;	
+	double easting, northing;	
+
+	assert (*e == 'B');
+
+	m_dao[2] = e[0];
+	m_dao[3] = e[1];	/* Type of location.  e.g.  !TB6! */
+				/* Will be changed by point types. */
+
+	len = strlen(e);
+
+	ipat = find_ttloc_match (e, xstr, ystr, zstr, bstr, dstr);
+	if (ipat >= 0) {
+
+	  //dw_printf ("ipat=%d, x=%s, y=%s, b=%s, d=%s\n", ipat, xstr, ystr, bstr, dstr);
+
+	  switch (tt_config.ttloc_ptr[ipat].type) {
+	    case TTLOC_POINT:
+		
+	      m_latitude = tt_config.ttloc_ptr[ipat].point.lat;
+	      m_longitude = tt_config.ttloc_ptr[ipat].point.lon;
+
+	      /* Is it one of ten or a hundred positions? */
+	      /* It's not hardwired to always be B0n or B9nn.  */
+	      /* This is a pretty good approximation. */
+
+	      if (strlen(e) == 3) {	/* probably B0n -->  !Tn ! */
+		m_dao[2] = e[2];
+	        m_dao[3] = ' ';
+	      }
+	      if (strlen(e) == 4) {	/* probably B9nn -->  !Tnn! */
+		m_dao[2] = e[2];
+	        m_dao[3] = e[3];
+	      }
+	      break;
+
+	    case TTLOC_VECTOR:
+
+	      if (strlen(bstr) != 3) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Bearing \"%s\" should be 3 digits.\n", bstr);
+	        // return error code?
+	      }
+	      if (strlen(dstr) < 1) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Distance \"%s\" should 1 or more digits.\n", dstr);
+	        // return error code?
+	      }
+
+	      lat0 = D2R(tt_config.ttloc_ptr[ipat].vector.lat);
+	      lon0 = D2R(tt_config.ttloc_ptr[ipat].vector.lon);
+	      dist = atof(dstr) * tt_config.ttloc_ptr[ipat].vector.scale;
+	      bearing = D2R(atof(bstr));
+
+	      /* Equations and caluculators found here: */
+	      /* http://movable-type.co.uk/scripts/latlong.html */
+
+	      m_latitude = R2D(asin(sin(lat0) * cos(dist/R) + cos(lat0) * sin(dist/R) * cos(bearing)));
+
+	      m_longitude = R2D(lon0 + atan2(sin(bearing) * sin(dist/R) * cos(lat0),
+				  cos(dist/R) - sin(lat0) * sin(D2R(m_latitude))));
+	      break;
+
+	    case TTLOC_GRID:
+
+	      if (strlen(xstr) == 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Missing X coordinate.\n");
+		strcpy (xstr, "0");
+	      }
+	      if (strlen(ystr) == 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Missing Y coordinate.\n");
+		strcpy (ystr, "0");
+	      }
+
+	      lat0 = tt_config.ttloc_ptr[ipat].grid.lat0;
+	      lat9 = tt_config.ttloc_ptr[ipat].grid.lat9;
+	      y = atof(ystr);
+	      m_latitude = lat0 + y * (lat9-lat0) / (pow(10., strlen(ystr)) - 1.);
+
+	      lon0 = tt_config.ttloc_ptr[ipat].grid.lon0;
+	      lon9 = tt_config.ttloc_ptr[ipat].grid.lon9;
+	      x = atof(xstr);
+	      m_longitude = lon0 + x * (lon9-lon0) / (pow(10., strlen(xstr)) - 1.);
+
+	      break;
+
+	    case TTLOC_UTM:
+
+	      if (strlen(xstr) == 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Missing X coordinate.\n");
+	        /* Avoid divide by zero later.  Put in middle of range. */
+		strcpy (xstr, "5");
+	      }
+	      if (strlen(ystr) == 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Missing Y coordinate.\n");
+	        /* Avoid divide by zero later.  Put in middle of range. */
+		strcpy (ystr, "5");
+	      }
+
+	      x = atof(xstr);
+	      easting = x * tt_config.ttloc_ptr[ipat].utm.scale + tt_config.ttloc_ptr[ipat].utm.x_offset;
+
+	      y = atof(ystr);
+	      northing = y * tt_config.ttloc_ptr[ipat].utm.scale + tt_config.ttloc_ptr[ipat].utm.y_offset;
+
+	      UTMtoLL (WSG84, northing, easting, tt_config.ttloc_ptr[ipat].utm.zone, 
+					&m_latitude, &m_longitude);
+	      break;
+
+	    default:
+	      assert (0);
+	  }
+	  return (0);
+	}
+
+	/* Send reject sound. */
+	/* Does not match any location specification. */
+
+	return (TT_ERROR_INVALID_LOC);
+
+} /* end parse_location */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        find_ttloc_match
+ *
+ * Purpose:     Try to match the received position report to a pattern
+ *		defined in the configuration file.
+ *
+ * Inputs:      e		- An "entry" extracted from a complete
+ *				  APRStt messsage.
+ *				  In this case, it should start with "B".
+ *
+ * Outputs:	xstr		- All digits matching x positions in configuration.
+ *		ystr		-                     y 
+ *		zstr		-                     z 
+ *		bstr		-                     b
+ * 		dstr		-                     d
+ *
+ * Returns:     >= 0 for index into table if found.
+ *		-1 if not found.
+ *
+ * Description:	
+ *
+ *----------------------------------------------------------------*/
+
+static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *bstr, char *dstr)
+{
+	int ipat;	/* Index into patterns from configuration file */
+	int len;	/* Length of pattern we are trying to match. */
+	int match;
+	char mc;
+	int k;
+
+//TODO: remove dw_printf ("find_ttloc_match: e=%s\n", e);
+
+	for (ipat=0; ipat<tt_config.ttloc_len; ipat++) {
+	  
+	  len = strlen(tt_config.ttloc_ptr[ipat].pattern);
+
+	  if (strlen(e) == len) {
+
+	    match = 1;
+	    strcpy (xstr, "");
+	    strcpy (ystr, "");
+	    strcpy (zstr, "");
+	    strcpy (bstr, "");
+	    strcpy (dstr, "");
+
+	    for (k=0; k<len; k++) {
+	      mc = tt_config.ttloc_ptr[ipat].pattern[k];
+	      switch (mc) {
+
+	        case 'B':
+	        case '0':
+	        case '1':
+	        case '2':
+	        case '3':
+	        case '4':
+	        case '5':
+	        case '6':
+	        case '7':
+	        case '8':
+	        case '9':
+		  if (e[k] != mc) {
+		    match = 0;
+		  }
+		  break;
+
+		case 'x':
+		   if (isdigit(e[k])) {
+	             char stemp[2];
+		     stemp[0] = e[k];
+		     stemp[1] = '\0';
+		     strcat (xstr, stemp);
+		   }
+		   else {
+		     match = 0;
+	           }
+		  break;
+
+		case 'y':
+		   if (isdigit(e[k])) {
+	             char stemp[2];
+		     stemp[0] = e[k];
+		     stemp[1] = '\0';
+		     strcat (ystr, stemp);
+		   }
+		   else {
+		     match = 0;
+	           }
+		  break;
+
+		case 'z':
+		   if (isdigit(e[k])) {
+	             char stemp[2];
+		     stemp[0] = e[k];
+		     stemp[1] = '\0';
+		     strcat (zstr, stemp);
+		   }
+		   else {
+		     match = 0;
+	           }
+		  break;
+
+		case 'b':
+		   if (isdigit(e[k])) {
+	             char stemp[2];
+		     stemp[0] = e[k];
+		     stemp[1] = '\0';
+		     strcat (bstr, stemp);
+		   }
+		   else {
+		     match = 0;
+	           }
+		  break;
+
+		case 'd':
+		   if (isdigit(e[k])) {
+	             char stemp[2];
+		     stemp[0] = e[k];
+		     stemp[1] = '\0';
+		     strcat (dstr, stemp);
+		   }
+		   else {
+		     match = 0;
+	           }
+		  break;
+
+		default:
+		  dw_printf ("find_ttloc_match: shouldn't be here.\n");
+		  /* Shouldn't be here. */
+		  match = 0;
+		  break;
+
+	      } /* switch */
+	    } /* for k */
+	  
+	    if (match) {
+	      return (ipat);
+	    }
+	  } /* if strlen */
+	}
+	return (-1);
+
+} /* end find_ttloc_match */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        parse_comment
+ *
+ * Purpose:     Extract comment / status or other special information from touch tone message. 
+ *
+ * Inputs:      e		- An "entry" extracted from a complete
+ *				  APRStt messsage.
+ *				  In this case, it should start with "C".
+ *
+ * Outputs:	m_comment
+ *		m_mic_e
+ *
+ * Returns:	0 for success or one of the TT_ERROR_... codes.
+ *
+ * Description:	We recognize these different formats:
+ *
+ *		Cn		- One digit for MIC-E position comment.
+ *				  Always / plus exactly 10 characters.
+ *	
+ *		Cnnnnnn		- Six digit frequency reformatted as nnn.nnnMHz
+ *
+ *		Cttt...tttt	- General comment in Multi-press encoding.
+ *
+ *----------------------------------------------------------------*/
+
+static int parse_comment (char *e)
+{
+	int len;
+	int n;
+
+	assert (*e == 'C');
+
+	len = strlen(e);
+
+	if (len == 2 && isdigit(e[1])) {
+	  m_mic_e = e[1];
+	  return (0);
+	}
+
+	if (len == 7 && isdigit(e[1]) && isdigit(e[2]) && isdigit(e[3]) && isdigit(e[4]) && isdigit(e[5]) && isdigit(e[6])) {
+	  m_freq[0] = e[1];
+	  m_freq[1] = e[2];
+	  m_freq[2] = e[3];
+	  m_freq[3] = '.';
+	  m_freq[4] = e[4];
+	  m_freq[5] = e[5];
+	  m_freq[6] = e[6];
+	  m_freq[7] = 'M';
+	  m_freq[8] = 'H';
+	  m_freq[9] = 'z';
+	  m_freq[10] = '\0';
+	  return (0);
+	}
+
+	tt_multipress_to_text (e+1, 0, m_comment);
+	return (0);
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        raw_tt_data_to_app
+ *
+ * Purpose:     Send raw touch tone data to application. 
+ *
+ * Inputs:      chan		- Channel where touch tone data heard.
+ *		msg		- String of button pushes.
+ *				  Normally ends with #.
+ *
+ * Global In:	m_callsign
+ *		m_symtab_or_overlay
+ *		m_symbol_code
+ *
+ * Returns:     None
+ *
+ * Description:	
+ * 		Put raw touch tone message in a packet and send to application.
+ * 		The APRS protocol does not have provision for this.
+ * 		For now, use the unused "t" message type.
+ * 		TODO:  Get an officially sanctioned method.
+ *
+ * 		Use callsign for source if available.
+ * 		Encode the overlay in the destination. 
+ *
+ *----------------------------------------------------------------*/
+
+
+static void raw_tt_data_to_app (int chan, char *msg)
+{
+
+#if TT_MAIN
+	return ;
+#else
+	char src[10], dest[10];
+	char raw_tt_msg[200];
+	packet_t pp;
+	char *c, *s;
+	int i;
+	int err;
+
+/* 
+ * "callsign" could be a general object name up to
+ * 9 characters and a space which will get ax25_from_text upset.
+ */
+
+	strcpy (src, "");
+	if (strlen(m_callsign) > 0) {
+	  for (c=m_callsign, s=src; *c != '\0' && strlen(src) < 6; c++) {
+	    if (isupper(*c) || isdigit(*c)) {
+	      *s++ = *c;
+	      *s = '\0';
+	    }
+	  }
+	}
+	else {
+	  strcpy (src, "APRSTT");
+	}
+
+
+	// TODO: test this.
+
+	err= symbols_into_dest (m_symtab_or_overlay, m_symbol_code, dest);
+	if (err) {
+	  /* Error message was already printed. */
+	  /* Set reasonable default rather than keeping "GPS???" which */
+	  /* is invalid and causes trouble later. */
+	
+	  strcpy (dest, "GPSAA");    
+	}
+
+	sprintf (raw_tt_msg, "%s>%s:t%s", src, dest, msg);
+
+	pp = ax25_from_text (raw_tt_msg, 1);
+
+/*
+ * Process like a normal received frame.
+ * NOTE:  This goes directly to application rather than
+ * thru the multi modem duplicate processing.
+ */
+
+	app_process_rec_packet (chan, -1, pp, -2, RETRY_NONE, "tt");
+
+#endif
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        main
+ *
+ * Purpose:     Unit test for this file.
+ *
+ * Description:	Run unit test like this:
+ *
+ *		rm a.exe ; gcc tt_text.c -DTT_MAIN -DDEBUG aprs_tt.c strtok_r.o utm/LatLong-UTMconversion.c ; ./a.exe 
+ *
+ *
+ * Bugs:	No automatic checking.
+ *		Just eyeball it to see if things look right.
+ *
+ *----------------------------------------------------------------*/
+
+
+#if TT_MAIN
+
+
+void text_color_set (dw_color_t c) { return; }
+
+int dw_printf (const char *fmt, ...) 
+{
+	va_list args;
+	int len;
+	
+	va_start (args, fmt);
+	len = vprintf (fmt, args);
+	va_end (args);
+	return (len);
+}
+
+
+
+int main (int argc, char *argv[])
+{
+	char text[256], buttons[256];
+	int n;
+
+	dw_printf ("Hello, world!\n");
+
+	aprs_tt_init (NULL);
+
+	//if (argc < 2) {
+	  //dw_printf ("Supply text string on command line.\n");
+	  //exit (1);
+	//}
+
+/* Callsigns & abbreviations. */
+
+	aprs_tt_message (0, "A9A2B42A7A7C71#");		/* WB4APR/7 */
+	aprs_tt_message (0, "A27773#");			/* abbreviated form */
+	/* Example in http://www.aprs.org/aprstt/aprstt-coding24.txt has a bad checksum! */
+	aprs_tt_message (0, "A27776#");			/* Expect error message. */
+
+	aprs_tt_message (0, "A2A7A7C71#");		/* Spelled suffix, overlay, checksum */
+	aprs_tt_message (0, "A27773#");			/* Suffix digits, overlay, checksum */
+
+	aprs_tt_message (0, "A9A2B26C7D9D71#");		/* WB2OSZ/7 numeric overlay */
+	aprs_tt_message (0, "A67979#");			/* abbreviated form */
+
+	aprs_tt_message (0, "A9A2B26C7D9D5A9#");	/* WB2OSZ/J letter overlay */
+	aprs_tt_message (0, "A6795A7#");		/* abbreviated form */
+
+	aprs_tt_message (0, "A277#");			/* Tactical call "277" no overlay and no checksum */
+
+/* Locations */
+
+	aprs_tt_message (0, "B01*A67979#"); 
+	aprs_tt_message (0, "B988*A67979#"); 
+
+	/* expect about 52.79  +0.83 */
+	aprs_tt_message (0, "B51000125*A67979#"); 
+
+	/* Try to get from Hilltop Tower to Archery & Target Range. */
+	/* Latitude comes out ok, 37.9137 -> 55.82 min. */
+	/* Longitude -81.1254 -> 8.20 min */
+
+	aprs_tt_message (0, "B5206070*A67979#"); 
+
+	aprs_tt_message (0, "B21234*A67979#"); 
+	aprs_tt_message (0, "B533686*A67979#"); 
+
+
+/* Comments */
+
+	aprs_tt_message (0, "C1");
+	aprs_tt_message (0, "C2");
+	aprs_tt_message (0, "C146520");
+	aprs_tt_message (0, "C7788444222550227776669660333666990122223333");
+
+/* Macros */
+
+	aprs_tt_message (0, "88345");
+
+	return(0);
+
+}  /* end main */
+
+
+
+
+#endif		
+
+/* end aprs_tt.c */
+
diff --git a/aprs_tt.h b/aprs_tt.h
new file mode 100755
index 0000000..9826e5f
--- /dev/null
+++ b/aprs_tt.h
@@ -0,0 +1,100 @@
+
+/* aprs_tt.h */
+
+#ifndef APRS_TT_H
+#define APRS_TT_H 1
+
+/*
+ * For holding location format specifications from config file.
+ * Same thing is also useful for macro definitions.
+ * We have exactly the same situation of looking for a pattern
+ * match and extracting fixed size groups of digits.
+ */
+
+struct ttloc_s {
+	enum { TTLOC_POINT, TTLOC_VECTOR, TTLOC_GRID, TTLOC_UTM, TTLOC_MACRO } type;
+
+	char pattern[20];	/* e.g. B998, B5bbbdddd, B2xxyy, Byyyxxx */
+				/* For macros, it should be all fixed digits, */
+				/* and the letters x, y, z.  e.g.  911, xxyyyz */
+
+	union {
+
+	  struct {	
+	    double lat;		/* Specific locations. */
+	    double lon;
+	  } point;
+
+	  struct {
+	    double lat;		/* For bearing/direction. */
+	    double lon;
+	    double scale;	/* conversion to meters */
+	  } vector;
+
+	  struct {
+	    double lat0;	/* yyy all zeros. */
+	    double lon0;	/* xxx */
+	    double lat9;	/* yyy all nines. */
+	    double lon9;	/* xxx */
+	  } grid;
+
+	  struct {
+	    char zone[8];
+	    double scale;
+	    double x_offset;
+	    double y_offset;
+	  } utm;
+
+	  struct {
+	    char *definition;
+	  } macro;
+	};
+};
+
+/* 
+ * Configuratin options for APRStt.
+ */
+
+#define TT_MAX_XMITS 10
+
+struct tt_config_s {
+
+	int obj_xmit_chan;		/* Channel to transmit object report. */
+	char obj_xmit_header[AX25_MAX_ADDRS*AX25_MAX_ADDR_LEN];	
+					/* e.g.  "WB2OSZ-5>APDW07,WIDE1-1" */
+	
+	int retain_time;		/* Seconds to keep information about a user. */
+	int num_xmits;			/* Number of times to transmit object report. */
+	int xmit_delay[TT_MAX_XMITS];	/* Delay between them. */
+
+	struct ttloc_s *ttloc_ptr;	/* Pointer to variable length array of above. */
+	int ttloc_size;			/* Number of elements allocated. */
+	int ttloc_len;			/* Number of elements actually used. */
+
+	double corral_lat;		/* The "corral" for unknown locations. */
+	double corral_lon;
+	double corral_offset;
+	int corral_ambiguity;
+};
+
+	
+void aprs_tt_init (struct tt_config_s *p_config);
+
+void aprs_tt_button (int chan, char button);
+
+/* Error codes for sending responses to user. */
+
+#define TT_ERROR_D_MSG		1	/* D was first char of field.  Not implemented yet. */
+#define TT_ERROR_INTERNAL	2	/* Internal error.  Shouldn't be here. */
+#define TT_ERROR_MACRO_NOMATCH	3	/* No definition for digit sequence. */
+#define TT_ERROR_BAD_CHECKSUM	4	/* Bad checksum on call. */
+#define TT_ERROR_INVALID_CALL	5	/* Invalid callsign. */
+#define TT_ERROR_INVALID_OBJNAME 6	/* Invalid object name. */
+#define TT_ERROR_INVALID_SYMBOL	7	/* Invalid symbol specification. */
+#define TT_ERROR_INVALID_LOC	8	/* Invalid location. */
+#define TT_ERROR_NO_CALL	9	/* No call or object name included. */
+
+
+#endif
+
+/* end aprs_tt.h */
\ No newline at end of file
diff --git a/atest.c b/atest.c
new file mode 100755
index 0000000..4fbea80
--- /dev/null
+++ b/atest.c
@@ -0,0 +1,447 @@
+
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        atest.c
+ *
+ * Purpose:     Unit test for the AFSK demodulator.
+ *
+ * Inputs:	Takes audio from a .WAV file insted of the audio device.
+ *
+ * Description:	This can be used to test the AFSK demodulator under
+ *		controlled and reproducable conditions for tweaking.
+ *	
+ *		For example
+ *
+ *		(1) Download WA8LMF's TNC Test CD image file from
+ *			http://wa8lmf.net/TNCtest/index.htm
+ *
+ *		(2) Burn a physical CD.
+ *
+ *		(3) "Rip" the desired tracks with Windows Media Player.
+ *			This results in .WMA files.
+ *		
+ *		(4) Upload the .WMA file(s) to http://media.io/ and
+ *			convert to .WAV format.
+ *
+ *
+ * Comparison to others:
+ *
+ *	Here are some other scores from Track 2 of the TNC Test CD:
+ *		http://sites.google.com/site/ki4mcw/Home/arduino-tnc
+ *
+ * 	Without ONE_CHAN defined:
+ *
+ *	  Notice that the number of packets decoded, as reported by
+ *	  this test program, will be twice the number expected because
+ *	  we are decoding the left and right audio channels separately.
+ *
+ *
+ * 	With ONE_CHAN defined:
+ *
+ *	  Only process one channel.  
+ *
+ *	  Version 0.4 decoded 870 packets.  
+ *
+ *	  After a little tweaking, version 0.5 decodes 931 packets.
+ *
+ *	  After more tweaking, version 0.6 gets 965 packets.
+ *	  This is without the option to retry after getting a bad FCS.
+ *
+ *--------------------------------------------------------------------*/
+
+// #define X 1
+
+
+#include <stdio.h>
+#include <unistd.h>
+//#include <fcntl.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <time.h>
+#include <getopt.h>
+
+
+#define ATEST_C 1
+
+#include "audio.h"
+#include "demod.h"
+// #include "fsk_demod_agc.h"
+#include "textcolor.h"
+#include "ax25_pad.h"
+#include "hdlc_rec2.h"
+
+
+
+struct wav_header {             /* .WAV file header. */
+        char riff[4];           /* "RIFF" */
+        int filesize;          /* file length - 8 */
+        char wave[4];           /* "WAVE" */
+        char fmt[4];            /* "fmt " */
+        int fmtsize;           /* 16. */
+        short wformattag;       /* 1 for PCM. */
+        short nchannels;        /* 1 for mono, 2 for stereo. */
+        int nsamplespersec;    /* sampling freq, Hz. */
+        int navgbytespersec;   /* = nblockalign*nsamplespersec. */
+        short nblockalign;      /* = wbitspersample/8 * nchannels. */
+        short wbitspersample;   /* 16 or 8. */
+        char data[4];           /* "data" */
+        int datasize;          /* number of bytes following. */
+} ;
+
+					/* 8 bit samples are unsigned bytes */
+					/* in range of 0 .. 255. */
+ 
+ 					/* 16 bit samples are signed short */
+					/* in range of -32768 .. +32767. */
+
+static struct wav_header header;
+static FILE *fp;
+static int e_o_f;
+static int packets_decoded = 0;
+static int decimate = 1;		/* Reduce that sampling rate. */
+					/* 1 = normal, 2 = half, etc. */
+
+
+int main (int argc, char *argv[])
+{
+
+	//int err;
+	int c;
+	struct audio_s modem;
+	int channel;
+	time_t start_time;
+
+	text_color_init(1);
+	text_color_set(DW_COLOR_INFO);
+
+/* 
+ * First apply defaults.
+ */
+	
+	memset (&modem, 0, sizeof(modem));
+
+	modem.num_channels = DEFAULT_NUM_CHANNELS;		
+	modem.samples_per_sec = DEFAULT_SAMPLES_PER_SEC;	
+	modem.bits_per_sample = DEFAULT_BITS_PER_SAMPLE;	
+
+	/* TODO: should have a command line option for this. */
+	/* Results v0.9: 971/69, 990/64, 992/65, 992/67, 1004/476 */
+
+	modem.fix_bits = RETRY_NONE;
+	modem.fix_bits = RETRY_SINGLE;
+	modem.fix_bits = RETRY_DOUBLE;
+	//modem.fix_bits = RETRY_TRIPLE;
+	//modem.fix_bits = RETRY_TWO_SEP;
+
+	for (channel=0; channel<MAX_CHANS; channel++) {
+
+	  modem.modem_type[channel] = AFSK;
+
+	  modem.mark_freq[channel] = DEFAULT_MARK_FREQ;		
+	  modem.space_freq[channel] = DEFAULT_SPACE_FREQ;		
+	  modem.baud[channel] = DEFAULT_BAUD;	
+	  strcpy (modem.profiles[channel], "C");	
+	// temp	
+	// strcpy (modem.profiles[channel], "F");		
+	  modem.num_subchan[channel] = strlen(modem.profiles[channel]);	
+
+	  modem.num_freq[channel] = 1;				
+	  modem.offset[channel] = 0;				
+// temp test
+	  //modem.num_subchan[channel] = modem.num_freq[channel] = 3;
+	  //modem.num_subchan[channel] = modem.num_freq[channel] = 5;				
+	  //modem.offset[channel] = 100;				
+
+	  //strcpy (modem.ptt_device[channel], "");
+	  //modem.ptt_line[channel] = PTT_NONE;
+
+	  //modem.slottime[channel] = DEFAULT_SLOTTIME;				
+	  //modem.persist[channel] = DEFAULT_PERSIST;				
+	  //modem.txdelay[channel] = DEFAULT_TXDELAY;				
+	  //modem.txtail[channel] = DEFAULT_TXTAIL;				
+	}
+
+	while (1) {
+          int this_option_optind = optind ? optind : 1;
+          int option_index = 0;
+          static struct option long_options[] = {
+            {"future1", 1, 0, 0},
+            {"future2", 0, 0, 0},
+            {"future3", 1, 0, 'c'},
+            {0, 0, 0, 0}
+          };
+
+	  /* ':' following option character means arg is required. */
+
+          c = getopt_long(argc, argv, "B:P:D:",
+                        long_options, &option_index);
+          if (c == -1)
+            break;
+
+          switch (c) {
+
+            case 'B':				/* -B for data Bit rate */
+						/*    300 implies 1600/1800 AFSK. */
+						/*    1200 implies 1200/2200 AFSK. */
+						/*    9600 implies scrambled. */
+
+              modem.baud[0] = atoi(optarg);
+
+              printf ("Data rate set to %d bits / second.\n", modem.baud[0]);
+
+              if (modem.baud[0] < 100 || modem.baud[0] > 10000) {
+                fprintf (stderr, "Use a more reasonable bit rate in range of 100 - 10000.\n");
+                exit (EXIT_FAILURE);
+              }
+	      if (modem.baud[0] < 600) {
+                modem.modem_type[0] = AFSK;
+                modem.mark_freq[0] = 1600;
+                modem.space_freq[0] = 1800;
+	      }
+	      else if (modem.baud[0] > 2400) {
+                modem.modem_type[0] = SCRAMBLE;
+                modem.mark_freq[0] = 0;
+                modem.space_freq[0] = 0;
+                printf ("Using scrambled baseband signal rather than AFSK.\n");
+	      }
+	      else {
+                modem.modem_type[0] = AFSK;
+                modem.mark_freq[0] = 1200;
+                modem.space_freq[0] = 2200;
+	      }
+              break;
+
+	    case 'P':				/* -P for modem profile. */
+
+	      printf ("Demodulator profile set to \"%s\"\n", optarg);
+	      strcpy (modem.profiles[0], optarg); 
+	      break;	
+
+	    case 'D':				/* -D reduce sampling rate for lower CPU usage. */
+
+	      decimate = atoi(optarg);
+	      printf ("Decimate factor = %d\n", decimate);
+	      modem.decimate[0] = decimate;
+	      break;	
+
+            case '?':
+
+              /* Unknown option message was already printed. */
+              //usage (argv);
+              break;
+
+            default:
+
+              /* Should not be here. */
+              printf("?? getopt returned character code 0%o ??\n", c);
+              //usage (argv);
+	  }
+        }
+    
+	if (optind >= argc) {
+	  printf ("Specify .WAV file name on command line.\n");
+	  exit (1);
+	}
+
+	fp = fopen(argv[optind], "rb");
+        if (fp == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+           fprintf (stderr, "Couldn't open file for read: %s\n", argv[optind]);
+	   //perror ("more info?");
+           exit (1);
+        }
+
+	start_time = time(NULL);
+
+
+/*
+ * Read the file header.  
+ */
+
+        fread (&header, sizeof(header), (size_t)1, fp);
+
+	assert (header.nchannels == 1 || header.nchannels == 2);
+	assert (header.wbitspersample == 8 || header.wbitspersample == 16);
+
+        modem.samples_per_sec = header.nsamplespersec;
+	modem.samples_per_sec = modem.samples_per_sec;
+	modem.bits_per_sample = header.wbitspersample;
+ 	modem.num_channels = header.nchannels;
+
+	text_color_set(DW_COLOR_INFO);
+	printf ("%d samples per second\n", modem.samples_per_sec);
+	printf ("%d bits per sample\n", modem.bits_per_sample);
+	printf ("%d audio channels\n", modem.num_channels);
+	printf ("%d audio bytes in file\n", (int)(header.datasize));
+
+		
+/*
+ * Initialize the AFSK demodulator and HDLC decoder.
+ */
+	multi_modem_init (&modem);
+
+
+	e_o_f = 0;
+	while ( ! e_o_f) 
+	{
+
+
+          int audio_sample;
+          int c;
+
+          for (c=0; c<modem.num_channels; c++)
+          {
+
+            /* This reads either 1 or 2 bytes depending on */
+            /* bits per sample.  */
+
+            audio_sample = demod_get_sample ();
+
+            if (audio_sample >= 256 * 256)
+              e_o_f = 1;
+
+#define ONE_CHAN 1              /* only use one audio channel. */
+
+#if ONE_CHAN
+            if (c != 0) continue;
+#endif
+
+            multi_modem_process_sample(c,audio_sample);
+          }
+
+                /* When a complete frame is accumulated, */
+                /* process_rec_frame, below, is called. */
+
+	}
+
+	text_color_set(DW_COLOR_INFO);
+	printf ("\n\n");
+	printf ("%d packets decoded in %d seconds.\n", packets_decoded, (int)(time(NULL) - start_time));
+
+	exit (0);
+}
+
+
+/*
+ * Simulate sample from the audio device.
+ */
+
+int audio_get (void)
+{
+	int ch;
+
+	ch = getc(fp);
+
+	if (ch < 0) e_o_f = 1;
+
+	return (ch);
+}
+
+
+
+/*
+ * Rather than queuing up frames with bad FCS, 
+ * try to fix them immediately.
+ */
+
+void rdq_append (rrbb_t rrbb)
+{
+	int chan;
+	int alevel;
+	int subchan;
+
+
+	chan = rrbb_get_chan(rrbb);
+	subchan = rrbb_get_subchan(rrbb);
+	alevel = rrbb_get_audio_level(rrbb);
+
+	hdlc_rec2_try_to_fix_later (rrbb, chan, subchan, alevel);
+}
+
+
+/*
+ * This is called when we have a good frame.
+ */
+
+void app_process_rec_packet (int chan, int subchan, packet_t pp, int alevel, retry_t retries, char *spectrum)  
+{	
+	
+	//int err;
+	//char *p;
+	char stemp[500];
+	unsigned char *pinfo;
+	int info_len;
+	int h;
+	char heard[20];
+	//packet_t pp;
+
+
+	packets_decoded++;
+
+
+	ax25_format_addrs (pp, stemp);
+
+	info_len = ax25_get_info (pp, &pinfo);
+
+	/* Print so we can see what is going on. */
+
+#if 1
+	/* Display audio input level. */
+        /* Who are we hearing?   Original station or digipeater. */
+
+	h = ax25_get_heard(pp);
+        ax25_get_addr_with_ssid(pp, h, heard);
+
+	text_color_set(DW_COLOR_DEBUG);
+	printf ("\n");
+
+	if (h != AX25_SOURCE) {
+	  printf ("Digipeater ");
+	}
+	printf ("%s audio level = %d   [%s]   %s\n", heard, alevel, retry_text[(int)retries], spectrum);
+
+
+#endif
+
+// Display non-APRS packets in a different color.
+
+	if (ax25_is_aprs(pp)) {
+	  text_color_set(DW_COLOR_REC);
+	  printf ("[%d] ", chan);
+	}
+	else {
+	  text_color_set(DW_COLOR_DEBUG);
+	  printf ("[%d] ", chan);
+	}
+
+	printf ("%s", stemp);			/* stations followed by : */
+	ax25_safe_print ((char *)pinfo, info_len, 0);
+	printf ("\n");
+
+	ax25_delete (pp);
+
+} /* end app_process_rec_packet */
+
+
+/* end atest.c */
diff --git a/audio.c b/audio.c
new file mode 100755
index 0000000..7e52f64
--- /dev/null
+++ b/audio.c
@@ -0,0 +1,1307 @@
+
+// Remove next line to eliminate annoying debug messages every 100 seconds.
+#define STATISTICS 1
+
+
+// 
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011, 2012, 2013, 2014  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      audio.c
+ *
+ * Purpose:   	Interface to audio device commonly called a "sound card" for
+ *		historical reasons.	
+ *
+ *		This version is for Linux and Cygwin.	
+ *
+ *		Two different types of sound interfaces are supported:
+ *
+ *		* OSS - For Cygwin or Linux versions with /dev/dsp.
+ *
+ *		* ALSA - For Linux versions without /dev/dsp.
+ *			In this case, define preprocessor symbol USE_ALSA.
+ *
+ * References:	Some tips on on using Linux sound devices.
+ *
+ *		http://www.oreilly.de/catalog/multilinux/excerpt/ch14-05.htm
+ *		http://cygwin.com/ml/cygwin-patches/2004-q1/msg00116/devdsp.c
+ *		http://manuals.opensound.com/developer/fulldup.c.html
+ *
+ *		"Introduction to Sound Programming with ALSA"
+ *		http://www.linuxjournal.com/article/6735?page=0,1
+ *
+ *		http://www.alsa-project.org/main/index.php/Asoundrc
+ *
+ * Credits:	Fabrice FAURE contributed code for the SDR UDP interface.
+ *
+ *		Discussion here:  http://gqrx.dk/doc/streaming-audio-over-udp
+ *
+ *
+ * Future:	Will probably rip out the OSS code.
+ *		ALSA was added to Linux kernel 10 years ago.
+ *		Cygwin doesn't have it but I see no reason to support Cygwin
+ *		now that we have a native Windows version.
+ *
+ *---------------------------------------------------------------*/
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+
+#if USE_ALSA
+#include <alsa/asoundlib.h>
+#else
+#include <sys/soundcard.h>
+#endif
+
+#include "direwolf.h"
+#include "audio.h"
+#include "textcolor.h"
+
+
+#if USE_ALSA
+static snd_pcm_t *audio_in_handle = NULL;
+static snd_pcm_t *audio_out_handle = NULL;
+
+static int bytes_per_frame;	/* number of bytes for a sample from all channels. */
+				/* e.g. 4 for stereo 16 bit. */
+
+static int set_alsa_params (snd_pcm_t *handle, struct audio_s *pa, char *name, char *dir);
+
+//static void alsa_select_device (char *pick_dev, int direction, char *result);
+#else
+
+static int oss_audio_device_fd = -1;	/* Single device, both directions. */
+
+#endif
+
+static int inbuf_size_in_bytes = 0;	/* number of bytes allocated */
+static unsigned char *inbuf_ptr = NULL;
+static int inbuf_len = 0;		/* number byte of actual data available. */
+static int inbuf_next = 0;		/* index of next to remove. */
+
+static int outbuf_size_in_bytes = 0;
+static unsigned char *outbuf_ptr = NULL;
+static int outbuf_len = 0;
+
+#define ONE_BUF_TIME 40
+		
+static enum audio_in_type_e audio_in_type;
+
+// UDP socket used for receiving data
+
+static int udp_sock;
+
+
+#define roundup1k(n) (((n) + 0x3ff) & ~0x3ff)
+#define calcbufsize(rate,chans,bits) roundup1k( ( (rate)*(chans)*(bits) / 8 * ONE_BUF_TIME)/1000  )
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_open
+ *
+ * Purpose:     Open the digital audio device.
+ *		For "OSS", the device name is typically "/dev/dsp".
+ *		For "ALSA", it's a lot more complicated.  See User Guide.
+ *
+ *		New in version 1.0, we recognize "udp:" optionally
+ *		followed by a port number.
+ *
+ * Inputs:      pa		- Address of structure of type audio_s.
+ *				
+ *				Using a structure, rather than separate arguments
+ *				seemed to make sense because we often pass around
+ *				the same set of parameters various places.
+ *
+ *				The fields that we care about are:
+ *					num_channels
+ *					samples_per_sec
+ *					bits_per_sample
+ *				If zero, reasonable defaults will be provided.
+ *
+ *				The device names are in adevice_in and adevice_out.
+ *				 - For "OSS", the device name is typically "/dev/dsp".
+ *				 - For "ALSA", the device names are hw:c,d
+ *				   where c is the "card" (for historical purposes)
+ *				   and d is the "device" within the "card."
+ *
+ *
+ * Outputs:	pa		- The ACTUAL values are returned here.
+ *
+ *				These might not be exactly the same as what was requested.
+ *					
+ *				Example: ask for stereo, 16 bits, 22050 per second.
+ *				An ordinary desktop/laptop PC should be able to handle this.
+ *				However, some other sort of smaller device might be
+ *				more restrictive in its capabilities.
+ *				It might say, the best I can do is mono, 8 bit, 8000/sec.
+ *
+ *				The sofware modem must use this ACTUAL information
+ *				that the device is supplying, that could be different
+ *				than what the user specified.
+ *              
+ * Returns:     0 for success, -1 for failure.
+ *		
+ *----------------------------------------------------------------*/
+
+int audio_open (struct audio_s *pa)
+{
+	int err;
+	int chan;
+
+#if USE_ALSA
+
+	char audio_in_name[30];
+	char audio_out_name[30];
+
+	assert (audio_in_handle == NULL);
+	assert (audio_out_handle == NULL);
+
+#else
+
+	assert (oss_audio_device_fd == -1);
+#endif
+
+/*
+ * Fill in defaults for any missing values.
+ */
+	if (pa -> num_channels == 0)
+	  pa -> num_channels = DEFAULT_NUM_CHANNELS;
+
+	if (pa -> samples_per_sec == 0)
+	  pa -> samples_per_sec = DEFAULT_SAMPLES_PER_SEC;
+
+	if (pa -> bits_per_sample == 0)
+	  pa -> bits_per_sample = DEFAULT_BITS_PER_SAMPLE;
+
+	for (chan=0; chan<MAX_CHANS; chan++) {
+	  if (pa -> mark_freq[chan] == 0)
+	    pa -> mark_freq[chan] = DEFAULT_MARK_FREQ;
+
+	  if (pa -> space_freq[chan] == 0)
+	    pa -> space_freq[chan] = DEFAULT_SPACE_FREQ;
+
+	  if (pa -> baud[chan] == 0)
+	    pa -> baud[chan] = DEFAULT_BAUD;
+
+	  if (pa->num_subchan[chan] == 0)
+	    pa->num_subchan[chan] = 1;
+	}
+
+/*
+ * Open audio device.
+ */
+
+	udp_sock = -1;
+
+	inbuf_size_in_bytes = 0;
+	inbuf_ptr = NULL;
+	inbuf_len = 0;
+	inbuf_next = 0;
+
+	outbuf_size_in_bytes = 0;
+	outbuf_ptr = NULL;
+	outbuf_len = 0;
+
+#if USE_ALSA
+
+/*
+ * Determine the type of audio input.
+ */
+	audio_in_type = AUDIO_IN_TYPE_SOUNDCARD; 	
+	
+	if (strcasecmp(pa->adevice_in, "stdin") == 0 || strcmp(pa->adevice_in, "-") == 0) {
+	  audio_in_type = AUDIO_IN_TYPE_STDIN;
+	  /* Change - to stdin for readability. */
+	  strcpy (pa->adevice_in, "stdin");
+	}
+	else if (strncasecmp(pa->adevice_in, "udp:", 4) == 0) {
+	  audio_in_type = AUDIO_IN_TYPE_SDR_UDP;
+	  /* Supply default port if none specified. */
+	  if (strcasecmp(pa->adevice_in,"udp") == 0 ||
+	    strcasecmp(pa->adevice_in,"udp:") == 0) {
+	    sprintf (pa->adevice_in, "udp:%d", DEFAULT_UDP_AUDIO_PORT);
+	  }
+	} 
+
+/* Let user know what is going on. */
+
+	/* If not specified, the device names should be "default". */
+
+	strcpy (audio_in_name, pa->adevice_in);
+	strcpy (audio_out_name, pa->adevice_out);
+
+        text_color_set(DW_COLOR_INFO);
+
+	if (strcmp(audio_in_name,audio_out_name) == 0) {
+          dw_printf ("Audio device for both receive and transmit: %s\n", audio_in_name);
+	}
+	else {
+          dw_printf ("Audio input device for receive: %s\n", audio_in_name);
+          dw_printf ("Audio out device for transmit: %s\n", audio_out_name);
+	}
+
+/*
+ * Now attempt actual opens.
+ */
+
+/*
+ * Input device.
+ */
+	switch (audio_in_type) {
+
+/*
+ * Soundcard - ALSA.
+ */
+	  case AUDIO_IN_TYPE_SOUNDCARD:
+
+	    err = snd_pcm_open (&audio_in_handle, audio_in_name, SND_PCM_STREAM_CAPTURE, 0);
+	    if (err < 0) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Could not open audio device %s for input\n%s\n", 
+	  		audio_in_name, snd_strerror(err));
+	      return (-1);
+	    }
+
+	    inbuf_size_in_bytes = set_alsa_params (audio_in_handle, pa, audio_in_name, "input");
+	    break;
+
+/*
+ * UDP.
+ */
+	  case AUDIO_IN_TYPE_SDR_UDP:
+
+	    //Create socket and bind socket
+	    
+	    {
+	      struct sockaddr_in si_me;
+	      int slen=sizeof(si_me);
+	      int data_size = 0;
+
+	      //Create UDP Socket
+	      if ((udp_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Couldn't create socket, errno %d\n", errno);
+	        return -1;
+	      }
+
+	      memset((char *) &si_me, 0, sizeof(si_me));
+	      si_me.sin_family = AF_INET;   
+	      si_me.sin_port = htons((short)atoi(audio_in_name+4));
+	      si_me.sin_addr.s_addr = htonl(INADDR_ANY);
+
+	      //Bind to the socket
+	      if (bind(udp_sock, (const struct sockaddr *) &si_me, sizeof(si_me))==-1) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Couldn't bind socket, errno %d\n", errno);
+	        return -1;
+	      }
+	    }
+	    inbuf_size_in_bytes = SDR_UDP_BUF_MAXLEN; 
+	
+	    break;
+
+/* 
+ * stdin.
+ */
+   	  case AUDIO_IN_TYPE_STDIN:
+
+	    /* Do we need to adjust any properties of stdin? */
+
+	    inbuf_size_in_bytes = 1024; 
+	
+	    break;
+
+	  default:
+
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Internal error, invalid audio_in_type\n");
+	    return (-1);
+  	}
+
+/*
+ * Output device.  Only "soundcard" is supported at this time. 
+ */
+	err = snd_pcm_open (&audio_out_handle, audio_out_name, SND_PCM_STREAM_PLAYBACK, 0);
+
+	if (err < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not open audio device %s for output\n%s\n", 
+			audio_out_name, snd_strerror(err));
+	  return (-1);
+	}
+
+	outbuf_size_in_bytes = set_alsa_params (audio_out_handle, pa, audio_out_name, "output");
+
+	if (inbuf_size_in_bytes <= 0 || outbuf_size_in_bytes <= 0) {
+	  return (-1);
+	}
+
+
+
+
+#else /* end of ALSA case */
+
+
+#error OSS support will probably be removed.  Complain if you still care about OSS.
+
+	oss_audio_device_fd = open (pa->adevice_in, O_RDWR);
+
+	if (oss_audio_device_fd < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("%s:\n", pa->adevice_in);
+	  sprintf (message, "Could not open audio device %s", pa->adevice_in);
+	  perror (message);
+	  return (-1);
+	}
+
+	outbuf_size_in_bytes = inbuf_size_in_bytes = set_oss_params (oss_audio_device_fd, pa);
+
+	if (inbuf_size_in_bytes <= 0 || outbuf_size_in_bytes <= 0) {
+	  return (-1);
+	}
+
+
+
+#endif	/* end of OSS case */
+
+
+/*
+ * Finally allocate buffer for each direction.
+ */
+	inbuf_ptr = malloc(inbuf_size_in_bytes);
+	assert (inbuf_ptr  != NULL);
+	inbuf_len = 0;
+	inbuf_next = 0;
+
+	outbuf_ptr = malloc(outbuf_size_in_bytes);
+	assert (outbuf_ptr  != NULL);
+	outbuf_len = 0;
+	
+	return (0);
+
+} /* end audio_open */
+
+
+
+
+#if USE_ALSA
+
+/*
+ * Set parameters for sound card.
+ *
+ * See  ??  for details. 
+ */
+/* 
+ * Terminology:
+ *   Sample	- for one channel.	e.g. 2 bytes for 16 bit.
+ *   Frame	- one sample for all channels.  e.g. 4 bytes for 16 bit stereo
+ *   Period	- size of one transfer.
+ */
+
+static int set_alsa_params (snd_pcm_t *handle, struct audio_s *pa, char *devname, char *inout)
+{
+
+	snd_pcm_hw_params_t *hw_params;
+	snd_pcm_uframes_t fpp; 		/* Frames per period. */
+
+	unsigned int val;
+
+	int dir;
+	int err;
+
+	int buf_size_in_bytes;			/* result, number of bytes per transfer. */
+
+
+	err = snd_pcm_hw_params_malloc (&hw_params);
+	if (err < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not alloc hw param structure.\n%s\n", 
+			snd_strerror(err));
+	  dw_printf ("for %s %s.\n", devname, inout);
+	  return (-1);
+	}
+
+	err = snd_pcm_hw_params_any (handle, hw_params);
+	if (err < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not init hw param structure.\n%s\n", 
+			snd_strerror(err));
+	  dw_printf ("for %s %s.\n", devname, inout);
+	  return (-1);
+	}
+
+	/* Interleaved data: L, R, L, R, ... */
+
+	err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
+
+	if (err < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not set interleaved mode.\n%s\n", 
+			snd_strerror(err));
+	  dw_printf ("for %s %s.\n", devname, inout);
+	  return (-1);
+	}
+
+	/* Signed 16 bit little endian or unsigned 8 bit. */
+
+
+	err = snd_pcm_hw_params_set_format (handle, hw_params,
+ 		pa->bits_per_sample == 8 ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16_LE);
+	if (err < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not set bits per sample.\n%s\n", 
+			snd_strerror(err));
+	  dw_printf ("for %s %s.\n", devname, inout);
+	  return (-1);
+	}
+
+	/* Number of audio channels. */
+
+
+	err = snd_pcm_hw_params_set_channels (handle, hw_params, pa->num_channels);
+	if (err < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not set number of audio channels.\n%s\n", 
+			snd_strerror(err));
+	  dw_printf ("for %s %s.\n", devname, inout);
+	  return (-1);
+	}
+
+	/* Audio sample rate. */
+
+
+	val = pa->samples_per_sec;
+
+	dir = 0;
+
+
+	err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &val, &dir);
+	if (err < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not set audio sample rate.\n%s\n", 
+			snd_strerror(err));
+	  dw_printf ("for %s %s.\n", devname, inout);
+	  return (-1);
+	}
+
+	if (val != pa->samples_per_sec) {
+
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf ("Asked for %d samples/sec but got %d.\n",
+
+				pa->samples_per_sec, val);
+	  dw_printf ("for %s %s.\n", devname, inout);
+
+	  pa->samples_per_sec = val;
+
+	}
+
+	/* Guessing around 20 reads/sec might be good. */
+	/* Period too long = too much latency. */
+	/* Period too short = too much overhead of many small transfers. */
+
+	fpp = pa->samples_per_sec / 20;
+
+#if DEBUG
+
+	text_color_set(DW_COLOR_DEBUG);
+
+
+	dw_printf ("suggest period size of %d frames\n", (int)fpp);
+
+#endif
+	dir = 0;
+	err = snd_pcm_hw_params_set_period_size_near (handle, hw_params, &fpp, &dir);
+
+	if (err < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not set period size\n%s\n", snd_strerror(err));
+	  dw_printf ("for %s %s.\n", devname, inout);
+	  return (-1);
+	}
+
+
+
+	err = snd_pcm_hw_params (handle, hw_params);
+	if (err < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not set hw params\n%s\n", snd_strerror(err));
+	  dw_printf ("for %s %s.\n", devname, inout);
+	  return (-1);
+	}
+
+
+	/* Driver might not like our suggested period size */
+	/* and might have another idea. */
+
+	err = snd_pcm_hw_params_get_period_size (hw_params, &fpp, NULL);
+	if (err < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not get audio period size.\n%s\n", snd_strerror(err));
+	  dw_printf ("for %s %s.\n", devname, inout);
+	  return (-1);
+	}
+
+	snd_pcm_hw_params_free (hw_params);
+	
+	/* A "frame" is one sample for all channels. */
+
+	/* The read and write use units of frames, not bytes. */
+
+	bytes_per_frame = snd_pcm_frames_to_bytes (handle, 1);
+	assert (bytes_per_frame == pa->num_channels * pa->bits_per_sample / 8);
+
+
+	buf_size_in_bytes = fpp * bytes_per_frame;
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("audio buffer size = %d (bytes per frame) x %d (frames per period) = %d \n", bytes_per_frame, (int)fpp, buf_size_in_bytes);
+#endif
+
+	return (buf_size_in_bytes);
+
+
+} /* end alsa_set_params */
+
+
+#else
+
+
+/*
+ * Set parameters for sound card.  (OSS only)
+ *
+ * See  /usr/include/sys/soundcard.h  for details. 
+ */
+
+static int set_oss_params (int fd, struct audio_s *pa) 
+{
+	int err;
+	int devcaps;
+	int asked_for;
+	char message[100];
+	int ossbuf_size_in_bytes;
+
+
+	err = ioctl (fd, SNDCTL_DSP_CHANNELS, &(pa->num_channels));
+   	if (err == -1) {
+	  text_color_set(DW_COLOR_ERROR);
+    	  perror("Not able to set audio device number of channels");
+ 	  return (-1);
+	}
+
+        asked_for = pa->samples_per_sec;
+
+	err = ioctl (fd, SNDCTL_DSP_SPEED, &(pa->samples_per_sec));
+   	if (err == -1) {
+	  text_color_set(DW_COLOR_ERROR);
+    	  perror("Not able to set audio device sample rate");
+ 	  return (-1);
+	}
+
+	if (pa->samples_per_sec != asked_for) {
+	  text_color_set(DW_COLOR_INFO);
+          dw_printf ("Asked for %d samples/sec but actually using %d.\n",
+		asked_for, pa->samples_per_sec);
+	}
+
+	/* This is actually a bit mask but it happens that */
+	/* 0x8 is unsigned 8 bit samples and */
+	/* 0x10 is signed 16 bit little endian. */
+
+	err = ioctl (fd, SNDCTL_DSP_SETFMT, &(pa->bits_per_sample));
+   	if (err == -1) {
+	  text_color_set(DW_COLOR_ERROR);
+    	  perror("Not able to set audio device sample size");
+ 	  return (-1);
+	}
+
+/*
+ * Determine capabilities.
+ */
+	err = ioctl (fd, SNDCTL_DSP_GETCAPS, &devcaps);
+   	if (err == -1) {
+	  text_color_set(DW_COLOR_ERROR);
+    	  perror("Not able to get audio device capabilities");
+ 	  // Is this fatal? //	return (-1);
+	}
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("audio_open(): devcaps = %08x\n", devcaps);	
+	if (devcaps & DSP_CAP_DUPLEX) dw_printf ("Full duplex record/playback.\n");
+	if (devcaps & DSP_CAP_BATCH) dw_printf ("Device has some kind of internal buffers which may cause delays.\n");
+	if (devcaps & ~ (DSP_CAP_DUPLEX | DSP_CAP_BATCH)) dw_printf ("Others...\n");
+#endif
+
+	if (!(devcaps & DSP_CAP_DUPLEX)) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Audio device does not support full duplex\n");
+    	  // Do we care? //	return (-1);
+	}
+
+	err = ioctl (fd, SNDCTL_DSP_SETDUPLEX, NULL);
+   	if (err == -1) {
+	  // text_color_set(DW_COLOR_ERROR);
+    	  // perror("Not able to set audio full duplex mode");
+ 	  // Unfortunate but not a disaster.
+	}
+
+/*
+ * Get preferred block size.
+ * Presumably this will provide the most efficient transfer.
+ *
+ * In my particular situation, this turned out to be
+ *  	2816 for 11025 Hz 16 bit mono
+ *	5568 for 11025 Hz 16 bit stereo
+ *     11072 for 44100 Hz 16 bit mono
+ *
+ * Your milage may vary.
+ */
+	err = ioctl (fd, SNDCTL_DSP_GETBLKSIZE, &ossbuf_size_in_bytes);
+   	if (err == -1) {
+	  text_color_set(DW_COLOR_ERROR);
+    	  perror("Not able to get audio block size");
+	  ossbuf_size_in_bytes = 2048;	/* pick something reasonable */
+	}
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("audio_open(): suggestd block size is %d\n", ossbuf_size_in_bytes);	
+#endif
+
+/*
+ * That's 1/8 of a second which seems rather long if we want to
+ * respond quickly.
+ */
+
+	ossbuf_size_in_bytes = calcbufsize(pa->samples_per_sec, pa->num_channels, pa->bits_per_sample);
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("audio_open(): using block size of %d\n", ossbuf_size_in_bytes);	
+#endif
+
+	assert (ossbuf_size_in_bytes >= 256 && ossbuf_size_in_bytes <= 32768);
+
+
+	return (ossbuf_size_in_bytes);
+
+} /* end set_oss_params */
+
+
+#endif
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_get
+ *
+ * Purpose:     Get one byte from the audio device.
+ *
+ * Returns:     0 - 255 for a valid sample.
+ *              -1 for any type of error.
+ *
+ * Description:	The caller must deal with the details of mono/stereo
+ *		and number of bytes per sample.
+ *
+ *		This will wait if no data is currently available.
+ *
+ *----------------------------------------------------------------*/
+
+// Use hot attribute for all functions called for every audio sample.
+
+__attribute__((hot))
+int audio_get (void)
+{
+	int n;
+	int retries = 0;
+
+#if STATISTICS
+	/* Gather numbers for read from audio device. */
+
+	static int duration = 100;	/* report every 100 seconds. */
+	static time_t last_time = 0;
+	time_t this_time;
+	static int sample_count;
+	static int error_count;
+#endif
+
+#if DEBUGx
+	text_color_set(DW_COLOR_DEBUG);
+
+	dw_printf ("audio_get():\n");
+
+#endif
+
+	assert (inbuf_size_in_bytes >= 100 && inbuf_size_in_bytes <= 32768);
+
+	  
+#if USE_ALSA
+
+	switch (audio_in_type) {
+
+/*
+ * Soundcard - ALSA 
+ */
+	  case AUDIO_IN_TYPE_SOUNDCARD:
+
+	    while (inbuf_next >= inbuf_len) {
+
+	      assert (audio_in_handle != NULL);
+#if DEBUGx
+	      text_color_set(DW_COLOR_DEBUG);
+	      dw_printf ("audio_get(): readi asking for %d frames\n", inbuf_size_in_bytes / bytes_per_frame);	
+#endif
+	      n = snd_pcm_readi (audio_in_handle, inbuf_ptr, inbuf_size_in_bytes / bytes_per_frame);
+
+#if DEBUGx	  
+	      text_color_set(DW_COLOR_DEBUG);
+	      dw_printf ("audio_get(): readi asked for %d and got %d frames\n",
+		inbuf_size_in_bytes / bytes_per_frame, n);	
+#endif
+
+#if STATISTICS
+	      if (last_time == 0) {
+	        last_time = time(NULL);
+	        sample_count = 0;
+	        error_count = 0;
+	      }
+	      else {
+	        if (n > 0) {
+	           sample_count += n;
+	        }
+	        else {
+	           error_count++;
+	        }
+	        this_time = time(NULL);
+	        if (this_time >= last_time + duration) {
+	          text_color_set(DW_COLOR_DEBUG);
+	          dw_printf ("\nPast %d seconds, %d audio samples, %d errors.\n\n", 
+			duration, sample_count, error_count);
+	          last_time = this_time;
+	          sample_count = 0;
+	          error_count = 0;
+	        }      
+	      }
+#endif
+ 
+	      if (n > 0) {
+
+	        /* Success */
+
+	        inbuf_len = n * bytes_per_frame;		/* convert to number of bytes */
+	        inbuf_next = 0;
+	      }
+	      else if (n == 0) {
+
+	        /* Didn't expect this, but it's not a problem. */
+	        /* Wait a little while and try again. */
+
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Audio input got zero bytes: %s\n", snd_strerror(n));
+	        SLEEP_MS(10);
+
+	        inbuf_len = 0;
+	        inbuf_next = 0;
+	      }
+	      else {
+	        /* Error */
+	        // TODO: Needs more study and testing. 
+
+		// TODO: print n.  should snd_strerror use n or errno?
+		// Audio input device error: Unknown error
+
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Audio input device error: %s\n", snd_strerror(n));
+
+	        /* Try to recover a few times and eventually give up. */
+	        if (++retries > 10) {
+	          inbuf_len = 0;
+	          inbuf_next = 0;
+	          return (-1);
+	        }
+
+	        if (n == -EPIPE) {
+
+	          /* EPIPE means overrun */
+
+	          snd_pcm_recover (audio_in_handle, n, 1);
+
+	        } 
+	        else {
+	          /* Could be some temporary condition. */
+	          /* Wait a little then try again. */
+	          /* Sometimes I get "Resource temporarily available" */
+	          /* when the Update Manager decides to run. */
+
+	          SLEEP_MS (250);
+	          snd_pcm_recover (audio_in_handle, n, 1);
+	        }
+	      }
+	    }
+	    break;
+
+/* 
+ * UDP.
+ */
+
+	  case AUDIO_IN_TYPE_SDR_UDP:
+
+	    while (inbuf_next >= inbuf_len) {
+	      int ch, res,i;
+
+              assert (udp_sock > 0);
+	      res = recv(udp_sock, inbuf_ptr, inbuf_size_in_bytes, 0);
+	      if (res < 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Can't read from udp socket, res=%d", res);
+	        inbuf_len = 0;
+	        inbuf_next = 0;
+	        return (-1);
+	      }
+	    
+	      inbuf_len = res;
+	      inbuf_next = 0;
+	    }
+	    break;
+
+/*
+ * stdin.
+ */
+	  case AUDIO_IN_TYPE_STDIN:
+
+	    while (inbuf_next >= inbuf_len) {
+	      int ch, res,i;
+
+	      res = read(STDIN_FILENO, inbuf_ptr, (size_t)inbuf_size_in_bytes);
+	      if (res <= 0) {
+	        text_color_set(DW_COLOR_INFO);
+	        dw_printf ("\nEnd of file on stdin.  Exiting.\n");
+	        exit (0);
+	      }
+	    
+	      inbuf_len = res;
+	      inbuf_next = 0;
+	    }
+
+	    break;
+	}
+
+
+
+#else	/* end ALSA, begin OSS */
+
+	while (audio_in_type == AUDIO_IN_TYPE_SOUNDCARD && inbuf_next >= inbuf_len) {
+	  assert (oss_audio_device_fd > 0);
+	  n = read (oss_audio_device_fd, inbuf_ptr, inbuf_size_in_bytes);
+	  //text_color_set(DW_COLOR_DEBUG);
+	  // dw_printf ("audio_get(): read %d returns %d\n", inbuf_size_in_bytes, n);	
+	  if (n < 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    perror("Can't read from audio device");
+	    inbuf_len = 0;
+	    inbuf_next = 0;
+	    return (-1);
+	  }
+	  inbuf_len = n;
+	  inbuf_next = 0;
+	}
+
+#endif	/* USE_ALSA */
+
+
+
+	if (inbuf_next < inbuf_len)
+	  n = inbuf_ptr[inbuf_next++];
+	//No data to read, avoid reading outside buffer
+	else
+	  n = 0;
+
+#if DEBUGx
+
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("audio_get(): returns %d\n", n);
+
+#endif
+ 
+
+	return (n);
+
+} /* end audio_get */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_put
+ *
+ * Purpose:     Send one byte to the audio device.
+ *
+ * Inputs:	c	- One byte in range of 0 - 255.
+ *
+ * Returns:     Normally non-negative.
+ *              -1 for any type of error.
+ *
+ * Description:	The caller must deal with the details of mono/stereo
+ *		and number of bytes per sample.
+ *
+ * See Also:	audio_flush
+ *		audio_wait
+ *
+ *----------------------------------------------------------------*/
+
+int audio_put (int c)
+{
+	/* Should never be full at this point. */
+	assert (outbuf_len < outbuf_size_in_bytes);
+
+	outbuf_ptr[outbuf_len++] = c;
+
+	if (outbuf_len == outbuf_size_in_bytes) {
+	  return (audio_flush());
+	}
+
+	return (0);
+
+} /* end audio_put */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_flush
+ *
+ * Purpose:     Push out any partially filled output buffer.
+ *
+ * Returns:     Normally non-negative.
+ *              -1 for any type of error.
+ *
+ * See Also:	audio_flush
+ *		audio_wait
+ *
+ *----------------------------------------------------------------*/
+
+int audio_flush (void)
+{
+#if USE_ALSA
+	int k;
+	char *psound;
+	int retries = 10;
+	snd_pcm_status_t *status;
+
+	assert (audio_out_handle != NULL);
+
+
+/*
+ * Trying to set the automatic start threshold didn't have the desired
+ * effect.  After the first transmitted packet, they are saved up
+ * for a few minutes and then all come out together.
+ *
+ * "Prepare" it if not already in the running state.
+ * We stop it at the end of each transmitted packet.
+ */
+
+
+	snd_pcm_status_alloca(&status);
+
+	k = snd_pcm_status (audio_out_handle, status);
+	if (k != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Audio output get status error.\n%s\n", snd_strerror(k));
+	}
+
+	if ((k = snd_pcm_status_get_state(status)) != SND_PCM_STATE_RUNNING) {
+
+	  //text_color_set(DW_COLOR_DEBUG);
+	  //dw_printf ("Audio output state = %d.  Try to start.\n", k);
+
+	  k = snd_pcm_prepare (audio_out_handle);
+
+	  if (k != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Audio output start error.\n%s\n", snd_strerror(k));
+	  }
+	}
+
+
+	psound = outbuf_ptr;
+
+	while (retries-- > 0) {
+
+	  k = snd_pcm_writei (audio_out_handle, psound, outbuf_len / bytes_per_frame);	
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("audio_flush(): snd_pcm_writei %d frames returns %d\n",
+				outbuf_len / bytes_per_frame, k);
+	  fflush (stdout);	
+#endif
+	  if (k == -EPIPE) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Audio output data underrun.\n");
+
+	    /* No problemo.  Recover and go around again. */
+
+	    snd_pcm_recover (audio_out_handle, k, 1);
+	  }
+ 	  else if (k < 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Audio write error: %s\n", snd_strerror(k));
+
+	    /* Some other error condition. */
+	    /* Try again. What do we have to lose? */
+
+	    snd_pcm_recover (audio_out_handle, k, 1);
+	  }
+ 	  else if (k != outbuf_len / bytes_per_frame) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Audio write took %d frames rather than %d.\n",
+ 			k, outbuf_len / bytes_per_frame);
+	
+	    /* Go around again with the rest of it. */
+
+	    psound += k * bytes_per_frame;
+	    outbuf_len -= k * bytes_per_frame;
+	  }
+	  else {
+	    /* Success! */
+	    outbuf_len = 0;
+	    return (0);
+	  }
+	}
+
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf ("Audio write error retry count exceeded.\n");
+
+	outbuf_len = 0;
+	return (-1);
+
+#else		/* OSS */
+
+	int k;
+	unsigned char *ptr;	
+	int len;
+
+	ptr = outbuf_ptr;
+	len = outbuf_len;
+
+	while (len > 0) {
+	  assert (oss_audio_device_fd > 0);
+	  k = write (oss_audio_device_fd, ptr, len);	
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("audio_flush(): write %d returns %d\n", len, k);
+	  fflush (stdout);	
+#endif
+	  if (k < 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    perror("Can't write to audio device");
+	    outbuf_len = 0;
+	    return (-1);
+	  }
+	  if (k < len) {
+	    /* presumably full but didn't block. */
+	    usleep (10000);
+	  }
+	  ptr += k;
+	  len -= k;
+	}
+
+	outbuf_len = 0;
+	return (0);
+#endif
+
+} /* end audio_flush */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_wait
+ *
+ * Purpose:     Wait until all the queued up audio out has been played.
+ *
+ * Inputs:	duration	- hint at number of milliseconds to wait.
+ *
+ * Returns:     Normally non-negative.
+ *              -1 for any type of error.
+ *
+ * Description:	In our particular application, we would want to make sure
+ *		that the entire packet has been sent out before turning
+ *		off the transmitter PTT control.
+ *
+ * In an ideal world:
+ * 
+ *		We would like to ask the hardware when all the queued
+ *		up sound has actually come out the speaker.
+ *		There is an OSS system call for this but it doesn't work
+ *		on Cygwin.  The application crashes at a later time.
+ *
+ *		Haven't yet verified correct operation with ALSA.
+ * 
+ * In reality:
+ *
+ *		Caller does the following:
+ *
+ *		(1) Make note of when PTT is turned on.
+ *		(2) Calculate how long it will take to transmit the 
+ *			frame including TXDELAY, frame (including 
+ *			"flags", data, FCS and bit stuffing), and TXTAIL.
+ *		(3) Add (1) and (2) resulting in when PTT should be turned off.
+ *		(4) Take difference between current time and PPT off time
+ *			and provide this as the additional delay required.
+ *
+ *----------------------------------------------------------------*/
+
+int audio_wait (int duration)
+{	
+	int err = 0;
+
+	audio_flush ();
+#if DEBUGx
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("audio_wait(): before sync, fd=%d\n", oss_audio_device_fd);	
+#endif
+
+#if USE_ALSA
+
+	//double t_enter, t_leave;
+	//int drain_ms;
+
+	//t_enter = dtime_now();
+
+	/* For playback, this should wait for all pending frames */
+	/* to be played and then stop. */
+
+	snd_pcm_drain (audio_out_handle);
+
+	//t_leave = dtime_now();
+	//drain_ms = (int)((t_leave - t_enter) * 1000.);
+
+	//text_color_set(DW_COLOR_DEBUG);
+	//dw_printf ("audio_wait():  suggested delay = %d ms, actual = %d\n",
+	//		duration, drain_ms);
+
+	/*
+ 	 * Experimentation reveals that snd_pcm_drain doesn't
+	 * actually wait.  It returns immediately. 
+	 * However it does serve a useful purpose of stopping
+	 * the playback after all the queued up data is used.
+ 	 *
+	 * Keep the sleep delay so PTT is not turned off too soon.
+ 	 */
+
+	if (duration > 0) {
+	  SLEEP_MS (duration);
+	}
+
+#else
+
+	assert (oss_audio_device_fd > 0);
+
+
+	// This causes a crash later on Cygwin.
+	// Haven't tried it on Linux yet.
+
+	// err = ioctl (oss_audio_device_fd, SNDCTL_DSP_SYNC, NULL);
+
+	if (duration > 0) {
+	  SLEEP_MS (duration);
+	}
+
+#endif
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("audio_wait(): after sync, status=%d\n", err);	
+#endif
+
+	return (err);
+
+} /* end audio_wait */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_close
+ *
+ * Purpose:     Close the audio device.
+ *
+ * Returns:     Normally non-negative.
+ *              -1 for any type of error.
+ *
+ *
+ *----------------------------------------------------------------*/
+
+int audio_close (void)
+{
+	int err = 0;
+
+#if USE_ALSA
+	assert (audio_in_handle != NULL);
+	assert (audio_out_handle != NULL);
+
+	audio_wait (0);
+
+	snd_pcm_close (audio_in_handle);
+	snd_pcm_close (audio_out_handle);
+
+#else
+	assert (oss_audio_device_fd > 0);
+
+	audio_wait (0);
+
+	close (oss_audio_device_fd);
+
+	oss_audio_device_fd = -1;
+#endif
+	free (inbuf_ptr);
+	free (outbuf_ptr);
+
+	inbuf_size_in_bytes = 0;
+	inbuf_ptr = NULL;
+	inbuf_len = 0;
+	inbuf_next = 0;
+
+	outbuf_size_in_bytes = 0;
+	outbuf_ptr = NULL;
+	outbuf_len = 0;
+
+	return (err);
+
+} /* end audio_close */
+
+
+/* end audio.c */
+
diff --git a/audio.h b/audio.h
new file mode 100755
index 0000000..a6eb535
--- /dev/null
+++ b/audio.h
@@ -0,0 +1,208 @@
+
+/*------------------------------------------------------------------
+ *
+ * Module:      audio.h
+ *
+ * Purpose:   	Interface to audio device commonly called a "sound card."
+ *		
+ *---------------------------------------------------------------*/
+
+
+#ifndef AUDIO_H
+#define AUDIO_H 1
+
+#include "direwolf.h"		/* for MAX_CHANS used throughout the application. */
+#include "hdlc_rec2.h"		/* for enum retry_e */
+				
+
+/*
+ * PTT control. 
+ */
+
+enum ptt_method_e { 
+	PTT_METHOD_NONE,	/* VOX or no transmit. */
+	PTT_METHOD_SERIAL,	/* Serial port RTS or DTR. */
+	PTT_METHOD_GPIO };	/* General purpos I/O. */
+
+typedef enum ptt_method_e ptt_method_t;
+
+enum ptt_line_e { PTT_LINE_RTS = 1, PTT_LINE_DTR = 2 };		
+typedef enum ptt_line_e ptt_line_t;
+
+enum audio_in_type_e {
+	AUDIO_IN_TYPE_SOUNDCARD,
+	AUDIO_IN_TYPE_SDR_UDP,
+	AUDIO_IN_TYPE_STDIN };
+
+struct audio_s {
+
+	/* Properites of the sound device. */
+
+	char adevice_in[80];		/* Name of the audio input device (or file?). */
+					/* TODO: Can be "-" to read from stdin. */
+
+	char adevice_out[80];		/* Name of the audio output device (or file?). */
+
+	int num_channels;		/* Should be 1 for mono or 2 for stereo. */
+	int samples_per_sec;		/* Audio sampling rate.  Typically 11025, 22050, or 44100. */
+	int bits_per_sample;		/* 8 (unsigned char) or 16 (signed short). */
+
+	enum audio_in_type_e audio_in_type;
+					/* Where is input (receive) audio coming from? */
+
+	/* Common to all channels. */
+
+	enum retry_e fix_bits;		/* Level of effort to recover from */
+					/* a bad FCS on the frame. */
+
+	/* Properties for each audio channel, common to receive and transmit. */
+	/* Can be different for each radio channel. */
+
+	enum modem_t {AFSK, NONE, SCRAMBLE} modem_type[MAX_CHANS];
+					/* Usual AFSK. */
+					/* Baseband signal. */
+					/* Scrambled http://www.amsat.org/amsat/articles/g3ruh/109/fig03.gif */
+
+	int decimate[MAX_CHANS];	/* Reduce AFSK sample rate by this factor to */
+					/* decrease computational requirements. */
+
+        int mark_freq[MAX_CHANS];	/* Two tones for AFSK modulation, in Hz. */
+	int space_freq[MAX_CHANS];	/* Standard tones are 1200 and 2200 for 1200 baud. */
+
+	int baud[MAX_CHANS];		/* Data bits (more accurately, symbols) per second. */
+					/* Standard rates are 1200 for VHF and 300 for HF. */
+
+	char profiles[MAX_CHANS][16];	/* 1 or more of ABC etc. */
+
+	int num_freq[MAX_CHANS];	/* Number of different frequency pairs for decoders. */
+
+	int offset[MAX_CHANS];		/* Spacing between filter frequencies. */
+
+	int num_subchan[MAX_CHANS];	/* Total number of modems / hdlc decoders for each channel. */
+					/* Potentially it could be product of strlen(profiles) * num_freq. */
+					/* Currently can't use both at once. */
+
+
+	/* Additional properties for transmit. */
+			
+	ptt_method_t ptt_method[MAX_CHANS];	/* serial port or GPIO. */
+
+	char ptt_device[MAX_CHANS][20];	/* Serial device name for PTT.  e.g. COM1 or /dev/ttyS0 */
+			
+	ptt_line_t ptt_line[MAX_CHANS];	/* Control line wehn using serial port.  */
+					/* PTT_RTS, PTT_DTR. */
+
+	int ptt_gpio[MAX_CHANS];	/* GPIO number. */
+
+	int ptt_invert[MAX_CHANS];	/* Invert the output. */
+
+	int slottime[MAX_CHANS];	/* Slot time in 10 mS units for persistance algorithm. */
+					/* Typical value is 10 meaning 100 milliseconds. */
+
+	int persist[MAX_CHANS];		/* Sets probability for transmitting after each */
+					/* slot time delay.  Transmit if a random number */
+					/* in range of 0 - 255 <= persist value.  */
+					/* Otherwise wait another slot time and try again. */
+					/* Default value is 63 for 25% probability. */
+
+	int txdelay[MAX_CHANS];		/* After turning on the transmitter, */
+					/* send "flags" for txdelay * 10 mS. */
+					/* Default value is 30 meaning 300 milliseconds. */
+
+	int txtail[MAX_CHANS];		/* Amount of time to keep transmitting after we */
+					/* are done sending the data.  This is to avoid */
+					/* dropping PTT too soon and chopping off the end */
+					/* of the frame.  Again 10 mS units. */
+					/* At this point, I'm thinking of 10 as the default. */
+};
+
+#if __WIN32__
+#define DEFAULT_ADEVICE	""		/* Windows: Empty string = default audio device. */
+#else
+#if USE_ALSA
+#define DEFAULT_ADEVICE	"default"	/* Use default device for ALSA. */
+#else
+#define DEFAULT_ADEVICE	"/dev/dsp"	/* First audio device for OSS. */
+#endif					
+#endif
+
+
+/*
+ * UDP audio receiving port.  Couldn't find any standard or usage precedent.
+ * Got the number from this example:   http://gqrx.dk/doc/streaming-audio-over-udp
+ * Any better suggestions?
+ */
+
+#define DEFAULT_UDP_AUDIO_PORT 7355
+
+
+// Maximum size of the UDP buffer (for allowing IP routing, udp packets are often limited to 1472 bytes)
+
+#define SDR_UDP_BUF_MAXLEN 2000
+
+
+
+#define DEFAULT_NUM_CHANNELS 	1
+#define DEFAULT_SAMPLES_PER_SEC	44100	/* Very early observations.  Might no longer be valid. */
+					/* 22050 works a lot better than 11025. */
+					/* 44100 works a little better than 22050. */
+					/* If you have a reasonable machine, use the highest rate. */
+#define MIN_SAMPLES_PER_SEC	8000
+#define MAX_SAMPLES_PER_SEC	48000	/* Formerly 44100. */
+					/* Software defined radio often uses 48000. */
+
+#define DEFAULT_BITS_PER_SAMPLE	16
+
+#define DEFAULT_FIX_BITS RETRY_SINGLE
+
+/* 
+ * Standard for AFSK on VHF FM. 
+ * Reversing mark and space makes no difference because
+ * NRZI encoding only cares about change or lack of change
+ * between the two tones.
+ *
+ * HF SSB uses 300 baud and 200 Hz shift.
+ * 1600 & 1800 Hz is a popular tone pair, sometimes 
+ * called the KAM tones.
+ */
+
+#define DEFAULT_MARK_FREQ	1200	
+#define DEFAULT_SPACE_FREQ	2200
+#define DEFAULT_BAUD		1200
+
+
+
+/*
+ * Typical transmit timings for VHF.
+ */
+
+#define DEFAULT_SLOTTIME	10
+#define DEFAULT_PERSIST		63
+#define DEFAULT_TXDELAY		30
+#define DEFAULT_TXTAIL		10	/* not sure yet. */
+
+
+/* 
+ * Note that we have two versions of these in audio.c and audio_win.c.
+ * Use one or the other depending on the platform.
+ */
+
+
+int audio_open (struct audio_s *pa);
+
+int audio_get (void);
+
+int audio_put (int c);
+
+int audio_flush (void);
+
+int audio_wait (int duration);
+
+int audio_close (void);
+
+
+#endif  /* ifdef AUDIO_H */
+
+
+/* end audio.h */
+
diff --git a/audio_win.c b/audio_win.c
new file mode 100755
index 0000000..fcd7a53
--- /dev/null
+++ b/audio_win.c
@@ -0,0 +1,1044 @@
+
+#define DEBUGUDP 1
+
+
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      audio.c
+ *
+ * Purpose:   	Interface to audio device commonly called a "sound card" for
+ *		historical reasons.		
+ *
+ *
+ *		This version uses the native Windows sound interface.
+ *
+ * Credits:	Fabrice FAURE contributed Linux code for the SDR UDP interface.
+ *
+ *		Discussion here:  http://gqrx.dk/doc/streaming-audio-over-udp
+ *
+ *---------------------------------------------------------------*/
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <ctype.h>
+#include <io.h>
+#include <fcntl.h>
+
+#include <windows.h>		
+#include <mmsystem.h>
+
+#ifndef WAVE_FORMAT_96M16
+#define WAVE_FORMAT_96M16 0x40000
+#define WAVE_FORMAT_96S16 0x80000
+#endif
+
+#include <winsock2.h>
+#define _WIN32_WINNT 0x0501
+#include <ws2tcpip.h>
+
+
+#include "direwolf.h"
+#include "audio.h"
+#include "textcolor.h"
+#include "ptt.h"
+
+
+
+/* 
+ * Allocate enough buffers for 1 second each direction. 
+ * Each buffer size is a trade off between being responsive 
+ * to activity on the channel vs. overhead of having too
+ * many little transfers.
+ */
+
+#define TOTAL_BUF_TIME 1000	
+#define ONE_BUF_TIME 40
+		
+#define NUM_IN_BUF ((TOTAL_BUF_TIME)/(ONE_BUF_TIME))
+#define NUM_OUT_BUF ((TOTAL_BUF_TIME)/(ONE_BUF_TIME))
+
+static enum audio_in_type_e audio_in_type;
+
+/*
+ * UDP socket for receiving audio stream.
+ * Buffer, length, and pointer for UDP or stdin.
+ */
+
+static SOCKET udp_sock;
+static char stream_data[SDR_UDP_BUF_MAXLEN];
+static int stream_len;
+static int stream_next;
+
+
+#define roundup1k(n) (((n) + 0x3ff) & ~0x3ff)
+#define calcbufsize(rate,chans,bits) roundup1k( ( (rate)*(chans)*(bits) / 8 * ONE_BUF_TIME)/1000  )
+
+
+/* For sound output. */
+/* out_wavehdr.dwUser is used to keep track of output buffer state. */
+
+#define DWU_FILLING 1		/* Ready to use or in process of being filled. */
+#define DWU_PLAYING 2		/* Was given to sound system for playing. */
+#define DWU_DONE 3		/* Sound system is done with it. */
+
+static HWAVEOUT audio_out_handle = 0;
+
+static volatile WAVEHDR out_wavehdr[NUM_OUT_BUF];
+static int out_current;		/* index to above. */
+static int outbuf_size;
+
+
+/* For sound input. */
+/* In this case dwUser is index of next available byte to remove. */
+
+static HWAVEIN  audio_in_handle = 0;
+static WAVEHDR in_wavehdr[NUM_IN_BUF];
+static volatile WAVEHDR *in_headp;	/* head of queue to process. */
+static CRITICAL_SECTION in_cs;
+
+
+
+
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        print_capabilities
+ *
+ * Purpose:     Display capabilities of the available audio devices.
+ *
+ * Example:
+ * 
+ *
+ *   Available audio input devices for receive (*=selected):
+ *     0: Microphone (Realtek High Defini  mono: 11 22 44 96  stereo: 11 22 44 96
+ *     1: Microphone (Bluetooth SCO Audio  mono: 11 22 44 96  stereo: 11 22 44 96
+ *     2: Microphone (Bluetooth AV Audio)  mono: 11 22 44 96  stereo: 11 22 44 96
+ *     3: Microphone (USB PnP Sound Devic  mono: 11 22 44 96  stereo: 11 22 44 96
+ *   Available audio output devices for transmit (*=selected):
+ *     0: Speakers (Realtek High Definiti  mono: 11 22 44 96  stereo: 11 22 44 96
+ *     1: Speakers (Bluetooth SCO Audio)   mono: 11 22 44 96  stereo: 11 22 44 96
+ *     2: Realtek Digital Output (Realtek  mono: 11 22 44 96  stereo: 11 22 44 96
+ *     3: Realtek Digital Output(Optical)  mono: 11 22 44 96  stereo: 11 22 44 96
+ *     4: Speakers (Bluetooth AV Audio)    mono: 11 22 44 96  stereo: 11 22 44 96
+ *     5: Speakers (USB PnP Sound Device)  mono: 11 22 44 96  stereo: 11 22 44 96	
+ *
+ *
+ * History:	Removed in version 0.9.
+ *
+ * Post Mortem discussion:
+ *
+ *		It turns out to be quite bogus and perhaps deceiving.
+ *
+ *		The chip (http://www.szlnst.com/Uploadfiles/HS100.pdf) in the cheap 
+ *		USB Audio device is physically capable of only 44.1 and 48 KHz
+ *		sampling rates.  Input is mono only.  Output is stereo only.
+ *		There is discussion of this in the Raspberry Pi document.
+ *
+ *		Here, it looks like it has much more general capabilities.
+ *		It seems the audio system puts some virtual layer on top of
+ *		it to provide resampling for different rates and silent 
+ *		right channel for stereo input.
+ *
+ *		
+ *----------------------------------------------------------------*/
+
+#if 0
+static void print_capabilities (DWORD formats) 
+{
+	dw_printf ("  mono:");
+	dw_printf ("%s", (formats & WAVE_FORMAT_1M16) ? " 11" : "   ");
+	dw_printf ("%s", (formats & WAVE_FORMAT_2M16) ? " 22" : "   ");
+	dw_printf ("%s", (formats & WAVE_FORMAT_4M16) ? " 44" : "   ");
+	dw_printf ("%s", (formats & WAVE_FORMAT_96M16) ? " 96" : "   ");
+
+	dw_printf ("  stereo:");
+	dw_printf ("%s", (formats & WAVE_FORMAT_1S16) ? " 11" : "   ");
+	dw_printf ("%s", (formats & WAVE_FORMAT_2S16) ? " 22" : "   ");
+	dw_printf ("%s", (formats & WAVE_FORMAT_4S16) ? " 44" : "   ");
+	dw_printf ("%s", (formats & WAVE_FORMAT_96S16) ? " 96" : "   ");
+}
+#endif
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_open
+ *
+ * Purpose:     Open the digital audio device.
+ *
+ *		New in version 1.0, we recognize "udp:" optionally
+ *		followed by a port number.
+ *
+ * Inputs:      pa		- Address of structure of type audio_s.
+ *				
+ *				Using a structure, rather than separate arguments
+ *				seemed to make sense because we often pass around
+ *				the same set of parameters various places.
+ *
+ *				The fields that we care about are:
+ *					num_channels
+ *					samples_per_sec
+ *					bits_per_sample
+ *				If zero, reasonable defaults will be provided.
+ *
+ * Outputs:	pa		- The ACTUAL values are returned here.
+ *
+ *				The Linux version adjusts strange values to the 
+ *				nearest valid value.  Don't know, yet, if Windows
+ *				does the same or just fails.  Or performs some
+ *				expensive resampling from a rate supported by
+ *				hardware.
+ *
+ *				These might not be exactly the same as what was requested.
+ *					
+ *				Example: ask for stereo, 16 bits, 22050 per second.
+ *				An ordinary desktop/laptop PC should be able to handle this.
+ *				However, some other sort of smaller device might be
+ *				more restrictive in its capabilities.
+ *				It might say, the best I can do is mono, 8 bit, 8000/sec.
+ *
+ *				The sofware modem must use this ACTUAL information
+ *				that the device is supplying, that could be different
+ *				than what the user specified.
+ * 
+ * Returns:     0 for success, -1 for failure.
+ *
+ * References:	Multimedia Reference
+ *
+ *		http://msdn.microsoft.com/en-us/library/windows/desktop/dd743606%28v=vs.85%29.aspx
+ *
+ *----------------------------------------------------------------*/
+
+
+static void CALLBACK in_callback (HWAVEIN handle, UINT msg, DWORD instance, DWORD param1, DWORD param2);
+static void CALLBACK out_callback (HWAVEOUT handle, UINT msg, DWORD instance, DWORD param1, DWORD param2);
+
+int audio_open (struct audio_s *pa)
+{
+	int err;
+	int chan;
+	int n;
+	int in_dev_no;
+	int out_dev_no;
+
+	WAVEFORMATEX wf;
+
+	int num_devices;
+	WAVEINCAPS wic;
+	WAVEOUTCAPS woc;
+
+	assert (audio_in_handle == 0);
+	assert (audio_out_handle == 0);
+
+
+/*
+ * Fill in defaults for any missing values.
+ */
+	if (pa -> num_channels == 0)
+	  pa -> num_channels = DEFAULT_NUM_CHANNELS;
+
+	if (pa -> samples_per_sec == 0)
+	  pa -> samples_per_sec = DEFAULT_SAMPLES_PER_SEC;
+
+	if (pa -> bits_per_sample == 0)
+	  pa -> bits_per_sample = DEFAULT_BITS_PER_SAMPLE;
+
+	for (chan=0; chan<MAX_CHANS; chan++) {
+	  if (pa -> mark_freq[chan] == 0)
+	    pa -> mark_freq[chan] = DEFAULT_MARK_FREQ;
+
+	  if (pa -> space_freq[chan] == 0)
+	    pa -> space_freq[chan] = DEFAULT_SPACE_FREQ;
+
+	  if (pa -> baud[chan] == 0)
+	    pa -> baud[chan] = DEFAULT_BAUD;
+
+	  if (pa->num_subchan[chan] == 0)
+	    pa->num_subchan[chan] = 1;
+	}
+
+	wf.wFormatTag = WAVE_FORMAT_PCM;
+	wf.nChannels = pa -> num_channels; 
+	wf.nSamplesPerSec = pa -> samples_per_sec;
+	wf.wBitsPerSample = pa -> bits_per_sample;
+	wf.nBlockAlign = (wf.wBitsPerSample / 8) * wf.nChannels;
+	wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
+	wf.cbSize = 0;
+
+	outbuf_size = calcbufsize(wf.nSamplesPerSec,wf.nChannels,wf.wBitsPerSample);
+
+
+	udp_sock = INVALID_SOCKET;
+
+	in_dev_no = WAVE_MAPPER;	/* = -1 */
+	out_dev_no = WAVE_MAPPER;
+
+/*
+ * Determine the type of audio input and select device.
+ */
+	
+	if (strcasecmp(pa->adevice_in, "stdin") == 0 || strcmp(pa->adevice_in, "-") == 0) {
+	  audio_in_type = AUDIO_IN_TYPE_STDIN;
+	  /* Change - to stdin for readability. */
+	  strcpy (pa->adevice_in, "stdin");
+	}
+	else if (strncasecmp(pa->adevice_in, "udp:", 4) == 0) {
+	  audio_in_type = AUDIO_IN_TYPE_SDR_UDP;
+	  /* Supply default port if none specified. */
+	  if (strcasecmp(pa->adevice_in,"udp") == 0 ||
+	    strcasecmp(pa->adevice_in,"udp:") == 0) {
+	    sprintf (pa->adevice_in, "udp:%d", DEFAULT_UDP_AUDIO_PORT);
+	  }
+	} 
+	else {
+	  audio_in_type = AUDIO_IN_TYPE_SOUNDCARD; 	
+
+	  /* Does config file have a number?  */
+	  /* If so, it is an index into list of devices. */
+
+	  if (strlen(pa->adevice_in) == 1 && isdigit(pa->adevice_in[0])) {
+	    in_dev_no = atoi(pa->adevice_in);
+	  }
+
+	  /* Otherwise, does it have search string? */
+
+	  if (in_dev_no == WAVE_MAPPER && strlen(pa->adevice_in) >= 1) {
+	    num_devices = waveInGetNumDevs();
+	    for (n=0 ; n<num_devices && in_dev_no == WAVE_MAPPER ; n++) {
+	      if ( ! waveInGetDevCaps(n, &wic, sizeof(WAVEINCAPS))) {
+	        if (strstr(wic.szPname, pa->adevice_in) != NULL) {
+	          in_dev_no = n;
+	        }
+	      }
+	    }
+	    if (in_dev_no == WAVE_MAPPER) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("\"%s\" doesn't match any of the input devices.\n", pa->adevice_in);
+	    }
+	  }
+ 	}
+
+/*
+ * Select output device.
+ */
+	if (strlen(pa->adevice_out) == 1 && isdigit(pa->adevice_out[0])) {
+	  out_dev_no = atoi(pa->adevice_out);
+	}
+
+	if (out_dev_no == WAVE_MAPPER && strlen(pa->adevice_out) >= 1) {
+	  num_devices = waveOutGetNumDevs();
+	  for (n=0 ; n<num_devices && out_dev_no == WAVE_MAPPER ; n++) {
+	    if ( ! waveOutGetDevCaps(n, &woc, sizeof(WAVEOUTCAPS))) {
+	      if (strstr(woc.szPname, pa->adevice_out) != NULL) {
+	        out_dev_no = n;
+	      }
+	    }
+	  }
+	  if (out_dev_no == WAVE_MAPPER) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("\"%s\" doesn't match any of the output devices.\n", pa->adevice_out);
+	  }
+	}
+ 
+
+/*
+ * Display what is available and anything selected.
+ */
+	text_color_set(DW_COLOR_INFO);
+	dw_printf ("Available audio input devices for receive (*=selected):\n");
+
+	num_devices = waveInGetNumDevs();
+	if (in_dev_no < -1 || in_dev_no >= num_devices) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Invalid input (receive) audio device number %d.\n", in_dev_no);
+	  in_dev_no = WAVE_MAPPER;
+	}
+	text_color_set(DW_COLOR_INFO);
+	for (n=0; n<num_devices; n++) {
+	  
+	  if ( ! waveInGetDevCaps(n, &wic, sizeof(WAVEINCAPS))) {
+	    dw_printf ("%c %d: %s\n", n==in_dev_no ? '*' : ' ', n, wic.szPname);
+	    //dw_printf ("%c %d: %-31s\n", n==in_dev_no ? '*' : ' ', n, wic.szPname);
+	    //print_capabilities (wic.dwFormats);
+	    //dw_printf ("\n");
+	  }
+	}
+
+	/* Display stdin or udp:port if appropriate. */
+
+	if (audio_in_type != AUDIO_IN_TYPE_SOUNDCARD) {
+	  dw_printf ("*    %s\n", pa->adevice_in);
+	}
+
+	dw_printf ("Available audio output devices for transmit (*=selected):\n");
+
+	/* TODO? */
+	/* No "*" is currently displayed when using the default device. */
+	/* Should we put "*" next to the default device when using it? */
+	/* Which is the default?  The first one? */
+
+	num_devices = waveOutGetNumDevs();
+	if (out_dev_no < -1 || out_dev_no >= num_devices) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Invalid output (transmit) audio device number %d.\n", out_dev_no);
+	  out_dev_no = WAVE_MAPPER;
+	}
+	text_color_set(DW_COLOR_INFO);
+	for (n=0; n<num_devices; n++) {
+	  
+	  if ( ! waveOutGetDevCaps(n, &woc, sizeof(WAVEOUTCAPS))) {
+	    dw_printf ("%c %d: %s\n", n==out_dev_no ? '*' : ' ', n, woc.szPname);
+	    //dw_printf ("%c %d: %-31s\n", n==out_dev_no ? '*' : ' ', n, woc.szPname);
+	    //print_capabilities (woc.dwFormats);
+	    //dw_printf ("\n");
+	  }
+	}
+
+	err = waveOutOpen (&audio_out_handle, out_dev_no, &wf, (DWORD_PTR)out_callback, 0, CALLBACK_FUNCTION);
+	if (err != MMSYSERR_NOERROR) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not open audio device for output.\n");
+	  return (-1);
+	}	  
+
+/*
+ * Set up the output buffers.
+ * We use dwUser to indicate it is available for filling.
+ */
+
+	memset ((void*)out_wavehdr, 0, sizeof(out_wavehdr));
+
+	for (n = 0; n < NUM_OUT_BUF; n++) {
+	  out_wavehdr[n].lpData = malloc(outbuf_size);
+	  out_wavehdr[n].dwUser = DWU_FILLING;	
+	  out_wavehdr[n].dwBufferLength = 0;
+	}
+	out_current = 0;			
+
+	
+/*
+ * Open audio input device.
+ */
+
+	switch (audio_in_type) {
+
+/*
+ * Soundcard.
+ */
+	  case AUDIO_IN_TYPE_SOUNDCARD:
+
+	    InitializeCriticalSection (&in_cs);
+
+	    err = waveInOpen (&audio_in_handle, in_dev_no, &wf, (DWORD_PTR)in_callback, 0, CALLBACK_FUNCTION);
+	    if (err != MMSYSERR_NOERROR) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Could not open audio device for input.\n");
+	      return (-1);
+	    }	  
+
+
+	    /*
+	     * Set up the input buffers.
+	     */
+
+	    memset ((void*)in_wavehdr, 0, sizeof(in_wavehdr));
+
+	    for (n = 0; n < NUM_OUT_BUF; n++) {
+	      in_wavehdr[n].dwBufferLength = outbuf_size;  /* all the same size */
+	      in_wavehdr[n].lpData = malloc(outbuf_size);
+	    }
+	    in_headp = NULL;			
+
+	    /*
+	     * Give them to the sound input system.
+	     */
+	
+	    for (n = 0; n < NUM_OUT_BUF; n++) {
+	      waveInPrepareHeader(audio_in_handle, &(in_wavehdr[n]), sizeof(WAVEHDR));
+	      waveInAddBuffer(audio_in_handle, &(in_wavehdr[n]), sizeof(WAVEHDR));
+	    }
+
+	    /*
+	     * Start it up.
+	     * The callback function is called when one is filled.
+	     */
+
+	    waveInStart (audio_in_handle);
+	    break;
+
+/*
+ * UDP.
+ */
+	  case AUDIO_IN_TYPE_SDR_UDP:
+
+	    {
+	      WSADATA wsadata;
+	      struct sockaddr_in si_me;
+	      //int slen=sizeof(si_me);
+	      //int data_size = 0;
+	      int err;
+
+	      err = WSAStartup (MAKEWORD(2,2), &wsadata);
+	      if (err != 0) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf("WSAStartup failed: %d\n", err);
+	          return (-1);
+	      }
+
+	      if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
+	        text_color_set(DW_COLOR_ERROR);
+                dw_printf("Could not find a usable version of Winsock.dll\n");
+                WSACleanup();
+                return (-1);
+	      }
+
+	      // Create UDP Socket
+
+	      udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	      if (udp_sock == INVALID_SOCKET) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Couldn't create socket, errno %d\n", WSAGetLastError());
+	        return -1;
+	      }
+
+	      memset((char *) &si_me, 0, sizeof(si_me));
+	      si_me.sin_family = AF_INET;   
+	      si_me.sin_port = htons((short)atoi(pa->adevice_in + 4));
+	      si_me.sin_addr.s_addr = htonl(INADDR_ANY);
+
+	      // Bind to the socket
+
+	      if (bind(udp_sock, (SOCKADDR *) &si_me, sizeof(si_me)) != 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Couldn't bind socket, errno %d\n", WSAGetLastError());
+	        return -1;
+	      }
+	      stream_next= 0;
+	      stream_len = 0;
+	    }
+
+	    break;
+
+/* 
+ * stdin.
+ */
+   	  case AUDIO_IN_TYPE_STDIN:
+
+  	    setmode (STDIN_FILENO, _O_BINARY);
+	    stream_next= 0;
+	    stream_len = 0;
+
+	    break;
+
+	  default:
+
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Internal error, invalid audio_in_type\n");
+	    return (-1);
+  	}
+
+	return (0);
+
+} /* end audio_open */
+
+
+
+/*
+ * Called when input audio block is ready.
+ */
+
+static void CALLBACK in_callback (HWAVEIN handle, UINT msg, DWORD instance, DWORD param1, DWORD param2)
+{
+	if (msg == WIM_DATA) {
+	
+	  WAVEHDR *p = (WAVEHDR*)param1;
+	  
+	  p->dwUser = -1;		/* needs to be unprepared. */
+	  p->lpNext = NULL;
+
+	  EnterCriticalSection (&in_cs);
+
+	  if (in_headp == NULL) {
+	    in_headp = p;		/* first one in list */
+	  }
+	  else {
+	    WAVEHDR *last = (WAVEHDR*)in_headp;
+
+	    while (last->lpNext != NULL) {
+	      last = last->lpNext;
+	    }
+	    last->lpNext = p;		/* append to last one */
+	  }
+
+	  LeaveCriticalSection (&in_cs);
+	}
+}
+
+/*
+ * Called when output system is done with a block and it
+ * is again available for us to fill.
+ */
+
+
+static void CALLBACK out_callback (HWAVEOUT handle, UINT msg, DWORD instance, DWORD param1, DWORD param2)
+{
+	if (msg == WOM_DONE) {   
+
+	  WAVEHDR *p = (WAVEHDR*)param1;
+	  
+	  p->dwBufferLength = 0;
+	  p->dwUser = DWU_DONE;
+	}
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_get
+ *
+ * Purpose:     Get one byte from the audio device.
+ *
+ * Returns:     0 - 255 for a valid sample.
+ *              -1 for any type of error.
+ *
+ * Description:	The caller must deal with the details of mono/stereo
+ *		and number of bytes per sample.
+ *
+ *		This will wait if no data is currently available.
+ *
+ *----------------------------------------------------------------*/
+
+// Use hot attribute for all functions called for every audio sample.
+
+__attribute__((hot))
+int audio_get (void)
+{
+	WAVEHDR *p;
+	int n;
+	int sample;
+
+#if DEBUGUDP
+	/* Gather numbers for read from UDP stream. */
+
+	static int duration = 100;	/* report every 100 seconds. */
+	static time_t last_time = 0;
+	time_t this_time;
+	static int sample_count;
+	static int error_count;
+#endif
+
+	switch (audio_in_type) {
+
+/*
+ * Soundcard.
+ */
+	  case AUDIO_IN_TYPE_SOUNDCARD:
+
+	    while (1) {
+
+	      /*
+	       * Wait if nothing available.
+	       * Could use an event to wake up but this is adequate.
+	       */
+	      int timeout = 25;
+
+	      while (in_headp == NULL) {
+	        SLEEP_MS (ONE_BUF_TIME / 5);
+	        timeout--;
+	        if (timeout <= 0) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("Audio input failure.\n");
+	          return (-1);
+	        }
+	      }
+
+	      p = (WAVEHDR*)in_headp;		/* no need to be volatile at this point */
+
+	      if (p->dwUser == -1) {
+	        waveInUnprepareHeader(audio_in_handle, p, sizeof(WAVEHDR));
+	        p->dwUser = 0;	/* Index for next byte. */
+	      }
+
+	      if (p->dwUser < p->dwBytesRecorded) {
+	        n = ((unsigned char*)(p->lpData))[p->dwUser++];
+#if DEBUGx
+
+	        text_color_set(DW_COLOR_DEBUG);
+	        dw_printf ("audio_get(): returns %d\n", n);
+
+#endif
+	        return (n);
+	      }
+	      /*
+	       * Buffer is all used up.  Give it back to sound input system.
+	       */
+
+	      EnterCriticalSection (&in_cs);
+	      in_headp = p->lpNext;
+	      LeaveCriticalSection (&in_cs);
+
+	      p->dwFlags = 0;
+	      waveInPrepareHeader(audio_in_handle, p, sizeof(WAVEHDR));
+	      waveInAddBuffer(audio_in_handle, p, sizeof(WAVEHDR));	  
+	    }
+	    break;
+/*
+ * UDP.
+ */
+	  case AUDIO_IN_TYPE_SDR_UDP:
+
+	    while (stream_next >= stream_len) {
+	      int res;
+
+              assert (udp_sock > 0);
+
+	      res = recv (udp_sock, stream_data, SDR_UDP_BUF_MAXLEN, 0);
+	      if (res <= 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Can't read from udp socket, errno %d", WSAGetLastError());
+	        stream_len = 0;
+	        stream_next = 0;
+	        return (-1);
+	      } 
+
+#if DEBUGUDP
+	      if (last_time == 0) {
+	        last_time = time(NULL);
+	        sample_count = 0;
+	        error_count = 0;
+	      }
+	      else {
+	        if (res > 0) {
+	           sample_count += res/2;
+	        }
+	        else {
+	           error_count++;
+	        }
+	        this_time = time(NULL);
+	        if (this_time >= last_time + duration) {
+	          text_color_set(DW_COLOR_DEBUG);
+	          dw_printf ("\nPast %d seconds, %d audio samples, %d errors.\n\n", 
+			duration, sample_count, error_count);
+	          last_time = this_time;
+	          sample_count = 0;
+	          error_count = 0;
+	        }      
+	      }
+#endif
+	      stream_len = res;
+	      stream_next = 0;
+	    }
+	    sample = stream_data[stream_next] & 0xff;
+	    stream_next++;
+	    return (sample);
+	    break;
+/* 
+ * stdin.
+ */
+   	  case AUDIO_IN_TYPE_STDIN:
+
+	    while (stream_next >= stream_len) {
+	      int res;
+
+	      res = read(STDIN_FILENO, stream_data, 1024);
+	      if (res <= 0) {
+	        text_color_set(DW_COLOR_INFO);
+	        dw_printf ("\nEnd of file on stdin.  Exiting.\n");
+	        exit (0);
+	      }
+	    
+	      stream_len = res;
+	      stream_next = 0;
+	    }
+	    return (stream_data[stream_next++] & 0xff);
+	    break;
+  	}
+
+	return (-1);
+
+} /* end audio_get */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_put
+ *
+ * Purpose:     Send one byte to the audio device.
+ *
+ * Inputs:	c	- One byte in range of 0 - 255.
+ *
+ *
+ * Global In:	out_current	- index of output buffer currenly being filled.
+ *
+ * Returns:     Normally non-negative.
+ *              -1 for any type of error.
+ *
+ * Description:	The caller must deal with the details of mono/stereo
+ *		and number of bytes per sample.
+ *
+ * See Also:	audio_flush
+ *		audio_wait
+ *
+ *----------------------------------------------------------------*/
+
+int audio_put (int c)
+{
+	WAVEHDR *p;
+	
+/* 
+ * Wait if no buffers are available.
+ * Don't use p yet because compiler might might consider dwFlags a loop invariant. 
+ */
+
+	int timeout = 10;
+	while ( out_wavehdr[out_current].dwUser == DWU_PLAYING) {
+	  SLEEP_MS (ONE_BUF_TIME);
+	  timeout--;
+	  if (timeout <= 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Audio output failure waiting for buffer.\n");
+	    ptt_term ();
+	    return (-1);
+	  }
+	}
+
+	p = (LPWAVEHDR)(&(out_wavehdr[out_current]));
+
+	if (p->dwUser == DWU_DONE) {
+	  waveOutUnprepareHeader (audio_out_handle, p, sizeof(WAVEHDR));
+	  p->dwBufferLength = 0;
+	  p->dwUser = DWU_FILLING;
+	}
+
+	/* Should never be full at this point. */
+
+	assert (p->dwBufferLength >= 0);
+	assert (p->dwBufferLength < outbuf_size);
+
+	p->lpData[p->dwBufferLength++] = c;
+
+	if (p->dwBufferLength == outbuf_size) {
+	  return (audio_flush());
+	}
+
+	return (0);
+
+} /* end audio_put */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_flush
+ *
+ * Purpose:     Send current buffer to the audio output system.
+ *
+ * Returns:     Normally non-negative.
+ *              -1 for any type of error.
+ *
+ * See Also:	audio_flush
+ *		audio_wait
+ *
+ *----------------------------------------------------------------*/
+
+int audio_flush (void)
+{
+	WAVEHDR *p;
+	MMRESULT e;
+	
+	
+	p = (LPWAVEHDR)(&(out_wavehdr[out_current]));
+
+	if (p->dwUser == DWU_FILLING && p->dwBufferLength > 0) {
+
+	  p->dwUser = DWU_PLAYING;
+
+	  waveOutPrepareHeader(audio_out_handle, p, sizeof(WAVEHDR));
+
+	  e = waveOutWrite(audio_out_handle, p, sizeof(WAVEHDR));
+	  if (e != MMSYSERR_NOERROR) {
+	    text_color_set (DW_COLOR_ERROR);
+	    dw_printf ("audio out write error %d\n", e);
+
+	    /* I don't expect this to ever happen but if it */
+	    /* does, make the buffer available for filling. */
+
+	    p->dwUser = DWU_DONE;
+	    return (-1);
+	  }
+	  out_current = (out_current + 1) % NUM_OUT_BUF;
+	}
+	return (0);
+
+} /* end audio_flush */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_wait
+ *
+ * Purpose:     Wait until all the queued up audio out has been played.
+ *
+ * Inputs:	duration	- hint at number of milliseconds to wait.
+ *
+ * Returns:     Normally non-negative.
+ *              -1 for any type of error.
+ *
+ * Description:	In our particular application, we want to make sure
+ *		that the entire packet has been sent out before turning
+ *		off the transmitter PTT control.
+ *
+ * In an ideal world:
+ * 
+ *		We would like to ask the hardware when all the queued
+ *		up sound has actually come out the speaker.
+ *
+ *		The original implementation (on Cygwin) tried using:
+ *		
+ *			ioctl (audio_device_fd, SNDCTL_DSP_SYNC, NULL);
+ *
+ *		but this caused the application to crash at a later time.
+ *
+ *		This might be revisited someday for the Windows version, 
+ *		but for now, we continue to use the work-around because it
+ *		works fine.
+ * 
+ * In reality:
+ *
+ *		Caller does the following:
+ *
+ *		(1) Make note of when PTT is turned on.
+ *		(2) Calculate how long it will take to transmit the 
+ *			frame including TXDELAY, frame (including 
+ *			"flags", data, FCS and bit stuffing), and TXTAIL.
+ *		(3) Add (1) and (2) resulting in when PTT should be turned off.
+ *		(4) Take difference between current time and PPT off time
+ *			and provide this as the additional delay required.
+ *
+ *----------------------------------------------------------------*/
+
+int audio_wait (int duration)
+{	
+	int err = 0;
+
+	audio_flush ();
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("audio_wait(): before sync, fd=%d\n", audio_device_fd);	
+#endif
+
+
+	if (duration > 0) {
+	  SLEEP_MS (duration);
+	}
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("audio_wait(): after sync, status=%d\n", err);	
+#endif
+
+	return (err);
+
+} /* end audio_wait */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_close
+ *
+ * Purpose:     Close the audio device.
+ *
+ * Returns:     Normally non-negative.
+ *              -1 for any type of error.
+ *
+ *
+ *----------------------------------------------------------------*/
+
+int audio_close (void)
+{
+	int err = 0;
+
+	int n;
+
+
+	assert (audio_in_handle != 0);
+	assert (audio_out_handle != 0);
+
+	audio_wait (0);
+
+/* Shutdown audio input. */
+
+	waveInReset(audio_in_handle); 
+	waveInStop(audio_in_handle);
+	waveInClose(audio_in_handle);
+	audio_in_handle = 0;
+
+	for (n = 0; n < NUM_IN_BUF; n++) {
+
+	  waveInUnprepareHeader (audio_in_handle, (LPWAVEHDR)(&(in_wavehdr[n])), sizeof(WAVEHDR));
+	  in_wavehdr[n].dwFlags = 0;
+	  free (in_wavehdr[n].lpData);
+ 	  in_wavehdr[n].lpData = NULL;
+	}
+
+	DeleteCriticalSection (&in_cs);
+
+
+/* Make sure all output buffers have been played then free them. */
+
+	for (n = 0; n < NUM_OUT_BUF; n++) {
+	  if (out_wavehdr[n].dwUser == DWU_PLAYING) {
+
+	    int timeout = 2 * NUM_OUT_BUF;
+	    while (out_wavehdr[n].dwUser == DWU_PLAYING) {
+	      SLEEP_MS (ONE_BUF_TIME);
+	      timeout--;
+	      if (timeout <= 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Audio output failure on close.\n");
+	      }
+	    }
+
+	    waveOutUnprepareHeader (audio_out_handle, (LPWAVEHDR)(&(out_wavehdr[n])), sizeof(WAVEHDR));
+
+	    out_wavehdr[n].dwUser = DWU_DONE;
+	  }
+	  free (out_wavehdr[n].lpData);
+ 	  out_wavehdr[n].lpData = NULL;
+	}
+
+	waveOutClose (audio_out_handle);
+	audio_out_handle = 0;
+
+	return (err);
+
+} /* end audio_close */
+
+/* end audio_win.c */
+
diff --git a/ax25_pad.c b/ax25_pad.c
new file mode 100755
index 0000000..1bd3451
--- /dev/null
+++ b/ax25_pad.c
@@ -0,0 +1,1722 @@
+// TODO:  Shouldn't this be using dw_printf???
+
+
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:	ax25_pad
+ *
+ * Purpose:	Packet assembler and disasembler.
+ *
+ *   		We can obtain AX.25 packets from different sources:
+ *		
+ *		(a) from an HDLC frame.
+ *		(b) from text representation.
+ *		(c) built up piece by piece.
+ *
+ *		We also want to use a packet in different ways:
+ *
+ *		(a) transmit as an HDLC frame.
+ *		(b) print in human-readable text.
+ *		(c) take it apart piece by piece.
+ *
+ *		Looking at the more general case, we might want to modify
+ *		an existing packet.  For instance an APRS repeater might 
+ *		want to change "WIDE2-2" to "WIDE2-1" and retransmit it.
+ *
+ *
+ * Description:	
+ *
+ *
+ *	A UI frame starts with 2-10 addressses (14-70 octets):
+ *
+ *	* Destination Address
+ *	* Source Address
+ *	* 0-8 Digipeater Addresses  (Could there ever be more as a result of 
+ *					digipeaters inserting their own call
+ *					and decrementing the remaining count in
+ *					WIDEn-n, TRACEn-n, etc.?   
+ *					NO.  The limit is 8 when transmitting AX.25 over the radio.
+ *					However, communication with an IGate server
+ *					could have a longer VIA path but that is in text form.)
+ *
+ *	Each address is composed of:
+ *
+ *	* 6 upper case letters or digits, blank padded.
+ *		These are shifted left one bit, leaving the the LSB always 0.
+ *	* a 7th octet containing the SSID and flags.
+ *		The LSB is always 0 except for the last octet of the address field.
+ *
+ *	The final octet of the Destination has the form:
+ *
+ *		C R R SSID 0, where,
+ *
+ *			C = command/response = 1
+ *			R R = Reserved = 1 1
+ *			SSID = substation ID
+ *			0 = zero
+ *
+ *	The final octet of the Source has the form:
+ *
+ *		C R R SSID 0, where,
+ *
+ *			C = command/response = 1
+ *			R R = Reserved = 1 1
+ *			SSID = substation ID
+ *			0 = zero (or 1 if no repeaters)
+ *
+ *	The final octet of each repeater has the form:
+ *
+ *		H R R SSID 0, where,
+ *
+ *			H = has-been-repeated = 0 initially.  
+ *				Set to 1 after this address has been used.
+ *			R R = Reserved = 1 1
+ *			SSID = substation ID
+ *			0 = zero (or 1 if last repeater in list)
+ *
+ *		A digipeater would repeat this frame if it finds its address
+ *		with the "H" bit set to 0 and all earlier repeater addresses
+ *		have the "H" bit set to 1.  
+ *		The "H" bit would be set to 1 in the repeated frame.
+ *
+ *	When monitoring, an asterisk is displayed after the last digipeater with 
+ *	the "H" bit set.  No asterisk means the source is being heard directly.
+ *
+ *	Example, if we can hear all stations involved,
+ *
+ *		SRC>DST,RPT1,RPT2,RPT3:		-- we heard SRC
+ *		SRC>DST,RPT1*,RPT2,RPT3:	-- we heard RPT1
+ *		SRC>DST,RPT1,RPT2*,RPT3:	-- we heard RPT2
+ *		SRC>DST,RPT1,RPT2,RPT3*:	-- we heard RPT3
+ *
+ *	
+ *	Next we have:
+ *
+ *	* One byte Control Field 	- APRS uses 3 for UI frame
+ *	* One byte Protocol ID 		- APRS uses 0xf0 for no layer 3
+ *
+ *	Finally the Information Field of 1-256 bytes.
+ *
+ *	And, of course, the 2 byte CRC.
+ *
+ *
+ * Constructors: ax25_init		- Clear everything.
+ *		ax25_from_text		- Tear apart a text string
+ *		ax25_from_frame		- Tear apart an AX.25 frame.  
+ *					  Must be called before any other function.
+ *
+ * Get methods:	....			- Extract destination, source, or digipeater
+ *					  address from frame.
+ *
+ * Assumptions:	CRC has already been verified to be correct.
+ *
+ *------------------------------------------------------------------*/
+
+#define AX25_PAD_C		/* this will affect behavior of ax25_pad.h */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <ctype.h>
+#ifndef _POSIX_C_SOURCE
+
+#define _POSIX_C_SOURCE 1
+#endif
+
+#include "regex.h"
+
+#if __WIN32__
+char *strtok_r(char *str, const char *delim, char **saveptr);
+#endif
+
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "fcs_calc.h"
+
+/*
+ * Accumulate statistics.
+ * If new_count gets more than a few larger than delete_count plus the size of 
+ * the transmit queue we have a memory leak.
+ */
+
+static int new_count = 0;
+static int delete_count = 0;
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_new
+ * 
+ * Purpose:	Allocate memory for a new packet object.
+ *
+ * Returns:	Identifier for a new packet object.
+ *		In the current implementation this happens to be a pointer.
+ *
+ *------------------------------------------------------------------------------*/
+
+
+static packet_t ax25_new (void) 
+{
+	struct packet_s *this_p;
+
+
+#if DEBUG
+        text_color_set(DW_COLOR_DEBUG);
+        dw_printf ("ax25_new(): before alloc, new=%d, delete=%d\n", new_count, delete_count);
+#endif
+
+	new_count++;
+
+/*
+ * check for memory leak.
+ */
+	if (new_count > delete_count + 100) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Memory leak for packet objects.  new=%d, delete=%d\n", new_count, delete_count);
+	}
+
+	this_p = calloc(sizeof (struct packet_s), (size_t)1);
+	this_p->magic1 = MAGIC;
+	this_p->magic2 = MAGIC;
+	return (this_p);
+}
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_delete
+ * 
+ * Purpose:	Destroy a packet object, freeing up memory it was using.
+ *
+ *------------------------------------------------------------------------------*/
+
+void ax25_delete (packet_t this_p)
+{
+#if DEBUG
+        text_color_set(DW_COLOR_DEBUG);
+        dw_printf ("ax25_delete(): before free, new=%d, delete=%d\n", new_count, delete_count);
+#endif
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	memset (this_p, 0, sizeof (struct packet_s));
+	delete_count++;
+	free (this_p);
+}
+
+
+		
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_from_text
+ * 
+ * Purpose:	Parse a frame in human-readable monitoring format and change
+ *		to internal representation.
+ *
+ * Input:	monitor	- "TNC-2" format of a monitored packet.  i.e.
+ *				source>dest[,repeater1,repeater2,...]:information
+ *
+ *		strict	- True to enforce rules for packets sent over the air.
+ *			  False to be more lenient for packets from IGate server.
+ *
+ * Returns:	Pointer to new packet object in the current implementation.
+ *
+ * Outputs:	Use the "get" functions to retrieve information in different ways.
+ *
+ *------------------------------------------------------------------------------*/
+
+packet_t ax25_from_text (char *monitor, int strict)
+{
+
+/*
+ * Tearing it apart is destructive so make our own copy first.
+ */
+	char stuff[512];
+
+	char *pinfo;
+	char *pa;
+	char *saveptr;		/* Used with strtok_r because strtok is not thread safe. */
+
+	static int first_time = 1;
+	static regex_t unhex_re;
+	int e;
+	char emsg[100];
+#define MAXMATCH 1
+	regmatch_t match[MAXMATCH];
+	int keep_going;
+	char temp[512];
+	int ssid_temp, heard_temp;
+
+
+	
+	packet_t this_p = ax25_new ();
+
+	/* Is it possible to have a nul character (zero byte) in the */
+	/* information field of an AX.25 frame? */
+
+	strcpy (stuff, monitor);
+
+/* 
+ * Translate hexadecimal values like <0xff> to non-printing characters.
+ * MIC-E message type uses 5 different non-printing characters.
+ */
+
+	if (first_time) 
+	{
+	  e = regcomp (&unhex_re, "<0x[0-9a-fA-F][0-9a-fA-F]>", 0);
+	  if (e) {
+	    regerror (e, &unhex_re, emsg, sizeof(emsg));
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("%s:%d: %s\n", __FILE__, __LINE__, emsg);
+	  }
+
+	  first_time = 0;
+	}
+
+#if 0
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("BEFORE: %s\n", stuff);
+	ax25_safe_print (stuff, -1, 0);
+	dw_printf ("\n");
+#endif
+	keep_going = 1;
+	while (keep_going) {
+	  if (regexec (&unhex_re, stuff, MAXMATCH, match, 0) == 0) {
+	    int n;
+	    char *p;
+  
+	    stuff[match[0].rm_so + 5] = '\0';
+	    n = strtol (stuff + match[0].rm_so + 3, &p, 16);
+	    stuff[match[0].rm_so] = n;
+	    strcpy (temp, stuff + match[0].rm_eo);
+	    strcpy (stuff + match[0].rm_so + 1, temp);
+	  }
+	  else {
+	    keep_going = 0;
+	  }
+	}
+#if 0
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("AFTER:  %s\n", stuff);
+	ax25_safe_print (stuff, -1, 0);
+	dw_printf ("\n");
+#endif
+
+/*
+ * Separate the addresses from the rest.
+ */
+	pinfo = strchr (stuff, ':');
+
+	if (pinfo == NULL) {
+	  ax25_delete (this_p);
+	  return (NULL);
+	}
+
+	*pinfo = '\0';
+	pinfo++;
+
+	if (strlen(pinfo) > AX25_MAX_INFO_LEN) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Warning: Information part truncated to %d characters.\n", AX25_MAX_INFO_LEN);
+	  pinfo[AX25_MAX_INFO_LEN] = '\0';
+	}
+	
+	strcpy ((char*)(this_p->the_rest + 2), pinfo);
+	this_p->the_rest_len = strlen(pinfo) + 2;
+
+/*
+ * Now separate the addresses.
+ * Note that source and destination order is swappped.
+ */
+
+	this_p->num_addr = 2;
+
+/*
+ * Source address.
+ * Don't use traditional strtok because it is not thread safe.
+ */
+	pa = strtok_r (stuff, ">", &saveptr);
+	if (pa == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Failed to create packet from text.  No source address\n");
+	  ax25_delete (this_p);
+	  return (NULL);
+	}
+
+	if ( ! ax25_parse_addr (pa, strict, this_p->addrs[AX25_SOURCE], &ssid_temp, &heard_temp)) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Failed to create packet from text.  Bad source address\n");
+	  ax25_delete (this_p);
+	  return (NULL);
+	}
+
+	this_p->ssid_etc[AX25_SOURCE] = SSID_H_MASK | SSID_RR_MASK;
+	ax25_set_ssid (this_p, AX25_SOURCE, ssid_temp);
+
+/*
+ * Destination address.
+ */
+ 
+	pa = strtok_r (NULL, ",", &saveptr);
+	if (pa == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Failed to create packet from text.  No destination address\n");
+	  ax25_delete (this_p);
+	  return (NULL);
+	}
+
+	if ( ! ax25_parse_addr (pa, strict, this_p->addrs[AX25_DESTINATION], &ssid_temp, &heard_temp)) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Failed to create packet from text.  Bad destination address\n");
+	  ax25_delete (this_p);
+	  return (NULL);
+	}
+
+	this_p->ssid_etc[AX25_DESTINATION] = SSID_H_MASK | SSID_RR_MASK;
+	ax25_set_ssid (this_p, AX25_DESTINATION, ssid_temp);
+
+/*
+ * VIA path.
+ */
+	while (( pa = strtok_r (NULL, ",", &saveptr)) != NULL && this_p->num_addr < AX25_MAX_ADDRS ) {
+
+	  //char *last;
+	  int k;
+
+	  k = this_p->num_addr;
+
+	  this_p->num_addr++;
+
+	  if ( ! ax25_parse_addr (pa, strict, this_p->addrs[k], &ssid_temp, &heard_temp)) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Failed to create packet from text.  Bad digipeater address\n");
+	    ax25_delete (this_p);
+	    return (NULL);
+	  }
+
+	  this_p->ssid_etc[k] = SSID_RR_MASK;
+	  ax25_set_ssid (this_p, k, ssid_temp);
+
+	  // Does it have an "*" at the end? 
+	  // TODO: Complain if more than one "*".
+	  // Could also check for all has been repeated bits are adjacent.
+	
+          if (heard_temp) {
+	    for ( ; k >= AX25_REPEATER_1; k--) {
+	      ax25_set_h (this_p, k);
+	    }
+	  }
+        }
+	
+	this_p->the_rest[0] = AX25_UI_FRAME;
+	this_p->the_rest[1] = AX25_NO_LAYER_3;
+
+	return (this_p);
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_from_frame
+ * 
+ * Purpose:	Split apart an HDLC frame to components.
+ *
+ * Inputs:	fbuf	- Pointer to beginning of frame.
+ *
+ *		flen	- Length excluding the two FCS bytes.
+ *
+ *		alevel	- Audio level of received signal.  
+ *			  Maximum range 0 - 100.
+ *			  -1 might be used when not applicable.
+ *
+ * Returns:	Pointer to new packet object or NULL if error.
+ *
+ * Outputs:	Use the "get" functions to retrieve information in different ways.
+ *
+ *------------------------------------------------------------------------------*/
+
+
+packet_t ax25_from_frame (unsigned char *fbuf, int flen, int alevel)
+{
+	unsigned char *pf;
+	//int found_last;
+	packet_t this_p;
+
+	int a;
+	int addr_bytes;
+	
+/*
+ * First make sure we have an acceptable length:
+ *
+ *	We are not concerned with the FCS (CRC) because someone else checked it.
+ *
+ * Is is possible to have zero length for info?  No.
+ */
+
+	if (flen < AX25_MIN_PACKET_LEN || flen > AX25_MAX_PACKET_LEN)
+	{
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Frame length %d not in allowable range of %d to %d.\n", flen, AX25_MIN_PACKET_LEN, AX25_MAX_PACKET_LEN);
+	  return (NULL);
+	}
+
+	this_p = ax25_new ();
+
+/*
+ * Extract the addresses.
+ * The last one has '1' in the LSB of the last byte.
+ */
+
+#if 1
+
+/* 
+ * 0.9 - Try new strategy that will allow KISS mode 
+ * to handle non AX.25 frame. 
+ */
+
+	this_p->num_addr = 0;		/* Number of addresses extracted. */
+	
+	addr_bytes = 0;
+	for (a = 0; a < flen && addr_bytes == 0; a++) {
+	  if (fbuf[a] & 0x01) {
+	    addr_bytes = a + 1;
+	  }
+	}
+
+	if (addr_bytes % 7 == 0) {
+	  int addrs = addr_bytes / 7;
+	  if (addrs >= AX25_MIN_ADDRS && addrs <= AX25_MAX_ADDRS) {
+	    this_p->num_addr = addrs;
+	    
+	    for (a = 0; a < addrs; a++){ 
+	      unsigned char *pin;
+	      char *pout;
+	      int j;
+	      char ch;
+	
+	      pin = fbuf + a * 7;
+              pout = & this_p->addrs[a][0];
+
+	      for (j=0; j<6; j++) 
+	      {
+	        ch = *pin++ >> 1;
+	        if (ch != ' ')
+	        {
+	          *pout++ = ch;
+	        }
+	      }
+	      *pout = '\0';
+
+	      this_p->ssid_etc[a] = *pin & ~ SSID_LAST_MASK;
+	    }
+	  }
+	}
+	
+	pf = fbuf + this_p->num_addr * 7;
+
+#else 
+
+	pf = fbuf;		/* Transmitted form from here. */
+
+	this_p->num_addr = 0;		/* Number of addresses extracted. */
+	found_last = 0;
+
+	while (this_p->num_addr < AX25_MAX_ADDRS && ! found_last) {
+
+	  unsigned char *pin;
+	  char *pout;
+	  int j;
+	  char ch;
+	
+	  pin = pf;
+          pout = & this_p->addrs[this_p->num_addr][0];
+
+	  for (j=0; j<6; j++) 
+	  {
+	    ch = *pin++ >> 1;
+	    if (ch != ' ')
+	    {
+	      *pout++ = ch;
+	    }
+	  }
+	  *pout = '\0';
+
+	  this_p->ssid_etc[this_p->num_addr] = pf[6] & ~ SSID_LAST_MASK;
+	
+  	  this_p->num_addr++;
+
+	  if (pf[6] & SSID_LAST_MASK) {	/* Is this the last one? */
+	    found_last = 1;
+          }
+	  else {
+	    pf += 7;			/* Get ready for next one. */
+	  }
+	}
+
+	if (this_p->num_addr < 2) {
+	  int k;
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Frame format error detected in ax25_from_frame, %s, line %d.\n", __FILE__, __LINE__);
+	  dw_printf ("Did not find a minimum of two addresses at beginning of AX.25 frame.\n");
+	  for (k=0; k<14; k++) {
+	    dw_printf (" %02x", fbuf[k]);
+	  }
+	  dw_printf ("\n");
+	  /* Should we keep going or delete the packet? */
+	}
+
+/*
+ * pf still points to the last address (repeater or source).
+ *
+ * Verify that it has the last address bit set.
+ */
+	if ((pf[6] & SSID_LAST_MASK) == 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Last address in header does not have LSB set.\n");
+	  ax25_delete (this_p);
+	  return (NULL);
+	}
+
+	pf += 7;
+
+#endif
+
+	if (this_p->num_addr * 7 > flen - 1) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Frame too short to include control field.\n");
+	  ax25_delete (this_p);
+	  return (NULL);
+	}
+
+
+
+
+/*
+ * pf should now point to control field.
+ * Previously we separated out control, PID, and Info here.
+ *
+ * Now (version 0.8) we save control, PID, and info together.
+ * This makes it easier to act as a dumb KISS TNC
+ * for AX.25-based protocols other than APRS.
+ */
+	this_p->the_rest_len = flen - (pf - fbuf);
+
+	assert (this_p->the_rest_len >= 1);
+
+	memcpy (this_p->the_rest, pf, (size_t)this_p->the_rest_len);
+	this_p->the_rest[this_p->the_rest_len] = '\0';
+
+	return (this_p);
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_dup
+ * 
+ * Purpose:	Make a copy of given packet object.
+ *
+ * Inputs:	copy_from	- Existing packet object.
+ *
+ * Returns:	Pointer to new packet object or NULL if error.
+ *
+ *
+ *------------------------------------------------------------------------------*/
+
+
+packet_t ax25_dup (packet_t copy_from)
+{
+
+	packet_t this_p;
+
+	
+	this_p = ax25_new ();
+
+	memcpy (this_p, copy_from, sizeof (struct packet_s));
+
+	return (this_p);
+
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_parse_addr
+ * 
+ * Purpose:	Parse address with optional ssid.
+ *
+ * Inputs:	in_addr		- Input such as "WB2OSZ-15*"
+ *
+ * 		strict		- TRUE for strict checking (6 characters, no lower case,
+ *				  SSID must be in range of 0 to 15).
+ *				  Strict is appropriate for packets sent
+ *				  over the radio.  Communication with IGate
+ *				  allows lower case (e.g. "qAR") and two 
+ *				  alphanumeric characters for the SSID.
+ *				  We also get messages like this from a server.
+ *					KB1POR>APU25N,TCPIP*,qAC,T2NUENGLD:...
+ *
+ * Outputs:	out_addr	- Address without any SSID.
+ *				  Must be at least AX25_MAX_ADDR_LEN bytes.
+ *
+ *		out_ssid	- Numeric value of SSID.
+ *
+ *		out_heard	- True if "*" found.
+ *
+ * Returns:	True (1) if OK, false (0) if any error.
+ *
+ *
+ *------------------------------------------------------------------------------*/
+
+
+int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard)
+{
+	char *p;
+	char sstr[4];
+	int i, j, k;
+	int maxlen;
+
+	strcpy (out_addr, "");
+	*out_ssid = 0;
+	*out_heard = 0;
+
+	maxlen = strict ? 6 : (AX25_MAX_ADDR_LEN-1);
+	p = in_addr;
+	i = 0;
+	for (p = in_addr; isalnum(*p); p++) {
+	  if (i >= maxlen) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Address is too long. \"%s\" has more than %d characters.\n", in_addr, maxlen);
+	    return 0;
+	  }
+	  out_addr[i++] = *p;
+	  out_addr[i] = '\0';
+	  if (strict && islower(*p)) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Address has lower case letters. \"%s\" must be all upper case.\n", in_addr);
+	    return 0;
+	  }
+	}
+	
+	strcpy (sstr, "");
+	j = 0;
+	if (*p == '-') {
+	  for (p++; isalnum(*p); p++) {
+	    if (j >= 2) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("SSID is too long. SSID part of \"%s\" has more than 2 characters.\n", in_addr);
+	      return 0;
+	    }
+	    sstr[j++] = *p;
+	    sstr[j] = '\0';
+	    if (strict && ! isdigit(*p)) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("SSID must be digits. \"%s\" has letters in SSID.\n", in_addr);
+	      return 0;
+	    }
+	  }
+	  k = atoi(sstr);
+	  if (k < 0 || k > 15) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("SSID out of range. SSID of \"%s\" not in range of 0 to 15.\n", in_addr);
+	    return 0;
+	  }
+	  *out_ssid = k;
+	}
+
+	if (*p == '*') {
+	  *out_heard = 1;
+	  p++;
+	}
+
+	if (*p != '\0') {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Invalid character \"%c\" found in address \"%s\".\n", *p, in_addr);
+	  return 0;
+	}
+
+	return (1);
+
+} /* end ax25_parse_addr */
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_unwrap_third_party
+ * 
+ * Purpose:	Unwrap a third party messge from the header.
+ *
+ * Inputs:	copy_from	- Existing packet object.
+ *
+ * Returns:	Pointer to new packet object or NULL if error.
+ *
+ * Example:	Input:		A>B,C:}D>E,F:info
+ *		Output:		D>E,F:info
+ *
+ *------------------------------------------------------------------------------*/
+
+packet_t ax25_unwrap_third_party (packet_t from_pp)
+{
+	unsigned char *info_p;
+	packet_t result_pp;
+
+	if (ax25_get_dti(from_pp) != '}') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Internal error: ax25_unwrap_third_party: wrong data type.\n");
+	  return (NULL);
+	}
+
+	(void) ax25_get_info (from_pp, &info_p);
+
+	result_pp = ax25_from_text((char *)info_p + 1, 0);
+
+	return (result_pp);
+}
+
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_set_addr
+ * 
+ * Purpose:	Add or change an address.
+ *
+ * Inputs:	n	- Index of address.   Use the symbols 
+ *			  AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
+ *		ad	- Address with optional dash and substation id.
+ *
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * TODO:  ax25_from_text could use this.
+ *
+ * Returns:	None.
+ *		
+ *
+ *------------------------------------------------------------------------------*/
+
+void ax25_set_addr (packet_t this_p, int n, char *ad)
+{
+	int ssid_temp, heard_temp;
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	assert (n >= 0 && n < AX25_MAX_ADDRS);
+	assert (strlen(ad) < AX25_MAX_ADDR_LEN);
+
+	if (n+1 > this_p->num_addr) {
+	  this_p->num_addr = n+1;
+	  this_p->ssid_etc[n] = SSID_RR_MASK;
+	}
+
+	ax25_parse_addr (ad, 0, this_p->addrs[n], &ssid_temp, &heard_temp);
+	ax25_set_ssid (this_p, n, ssid_temp);
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_insert_addr
+ * 
+ * Purpose:	Insert address at specified position, shifting others up one
+ *		position.
+ *		This is used when a digipeater wants to insert its own call
+ *		for tracing purposes.
+ *		For example:
+ *			W1ABC>TEST,WIDE3-3
+ *		Would become:
+ *			W1ABC>TEST,WB2OSZ-1*,WIDE3-2
+ *
+ * Inputs:	n	- Index of address.   Use the symbols 
+ *			  AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
+ *
+ *		ad	- Address with optional dash and substation id.
+ *
+ * Bugs:	Little validity or bounds checking is performed.  Be careful.
+ *		  
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	None.
+ *		
+ *
+ *------------------------------------------------------------------------------*/
+
+void ax25_insert_addr (packet_t this_p, int n, char *ad)
+{
+	int k;
+	int ssid_temp, heard_temp;
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	assert (n >= AX25_REPEATER_1 && n < AX25_MAX_ADDRS);
+	assert (strlen(ad) < AX25_MAX_ADDR_LEN);
+
+	/* Don't do it if we already have the maximum number. */
+	/* Should probably return success/fail code but currently the caller doesn't care. */
+
+	if ( this_p->num_addr >= AX25_MAX_ADDRS) {
+	  return;
+	}
+
+	/* Shift the current occupant and others up. */
+
+	for (k=this_p->num_addr; k>n; k--) {
+	  strcpy (this_p->addrs[k], this_p->addrs[k-1]);
+	  this_p->ssid_etc[k] = this_p->ssid_etc[k-1];
+	}
+
+	this_p->num_addr++;
+
+	ax25_parse_addr (ad, 0, this_p->addrs[n], &ssid_temp, &heard_temp);
+	this_p->ssid_etc[n] = SSID_RR_MASK;
+	ax25_set_ssid (this_p, n, ssid_temp);
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_remove_addr
+ * 
+ * Purpose:	Remove address at specified position, shifting others down one position.
+ *		This is used when we want to remove something from the digipeater list.
+ *
+ * Inputs:	n	- Index of address.   Use the symbols 
+ *			  AX25_REPEATER1, AX25_REPEATER2, etc.
+ *
+ * Bugs:	Little validity or bounds checking is performed.  Be careful.
+ *		  
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	None.
+ *		
+ *
+ *------------------------------------------------------------------------------*/
+
+void ax25_remove_addr (packet_t this_p, int n)
+{
+	int k;
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	assert (n >= AX25_REPEATER_1 && n < AX25_MAX_ADDRS);
+
+	/* Shift those beyond to fill this position. */
+
+	this_p->num_addr--;
+
+	for (k = n; k < this_p->num_addr; k++) {
+	  strcpy (this_p->addrs[k], this_p->addrs[k+1]);
+	  this_p->ssid_etc[k] = this_p->ssid_etc[k+1];
+	}
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_get_num_addr
+ * 
+ * Purpose:	Return number of addresses in current packet.
+ *
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	Number of addresses in the current packet.
+ *		Should be in the range of 2 .. AX25_MAX_ADDRS.
+ *
+ * Version 0.9:	Could be zero for a non AX.25 frame in KISS mode.
+ *
+ *------------------------------------------------------------------------------*/
+
+int ax25_get_num_addr (packet_t this_p)
+{
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	return (this_p->num_addr);
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_get_num_repeaters
+ * 
+ * Purpose:	Return number of repeater addresses in current packet.
+ *
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	Number of addresses in the current packet - 2.
+ *		Should be in the range of 0 .. AX25_MAX_ADDRS - 2.
+ *
+ *------------------------------------------------------------------------------*/
+
+int ax25_get_num_repeaters (packet_t this_p)
+{
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+
+	if (this_p->num_addr >= 2) {
+	  return (this_p->num_addr - 2);
+	}
+
+	return (0);
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_get_addr_with_ssid
+ * 
+ * Purpose:	Return specified address with any SSID in current packet.
+ *
+ * Inputs:	n	- Index of address.   Use the symbols 
+ *			  AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
+ *
+ * Outputs:	station - String representation of the station, including the SSID.
+ *			e.g.  "WB2OSZ-15"
+ *
+ * Bugs:	No bounds checking is performed.  Be careful.
+ *		  
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	Character string in usual human readable format,
+ *		
+ *
+ *------------------------------------------------------------------------------*/
+
+void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
+{	
+	int ssid;
+	char sstr[4];
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+
+/*
+ * This assert failure popped up once and it is not clear why.
+ * Let's print out more information about the situation so we 
+ * might have a clue about the root cause.
+ * Try to keep going instead of dying at this point.
+ */
+	//assert (n >= 0 && n < this_p->num_addr);
+
+	if (n < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Internal error detected in ax25_get_addr_with_ssid, %s, line %d.\n", __FILE__, __LINE__);
+	  dw_printf ("Address index, %d, is less than zero.\n", n);
+	  strcpy (station, "??????");
+	  return;
+	}
+
+	if (n >= this_p->num_addr) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Internal error detected in ax25_get_addr_with_ssid, %s, line %d.\n", __FILE__, __LINE__);
+	  dw_printf ("Address index, %d, is too large for number of addresses, %d.\n", n, this_p->num_addr);
+	  strcpy (station, "??????");
+	  return;
+	}
+
+	strcpy (station, this_p->addrs[n]);
+
+	ssid = ax25_get_ssid (this_p, n);
+	if (ssid != 0) {
+	  sprintf (sstr, "-%d", ssid);
+	  strcat (station, sstr);
+	}    
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_get_ssid
+ * 
+ * Purpose:	Return SSID of specified address in current packet.
+ *
+ * Inputs:	n	- Index of address.   Use the symbols 
+ *			  AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
+ *
+ * Warning:	No bounds checking is performed.  Be careful.
+ *		  
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	Substation id, as integer 0 .. 15.
+ *
+ * Bugs:	Rewrite to keep call and SSID separate internally.
+ *
+ *------------------------------------------------------------------------------*/
+
+int ax25_get_ssid (packet_t this_p, int n)
+{
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	assert (n >= 0 && n < this_p->num_addr);
+
+	return ((this_p->ssid_etc[n] & SSID_SSID_MASK) >> SSID_SSID_SHIFT);
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_set_ssid
+ * 
+ * Purpose:	Set the SSID of specified address in current packet.
+ *
+ * Inputs:	n	- Index of address.   Use the symbols 
+ *			  AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
+ *
+ *		ssid	- New SSID.  Must be in range of 0 to 15.
+ *
+ * Warning:	No bounds checking is performed.  Be careful.
+ *		  
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Bugs:	Rewrite to keep call and SSID separate internally.
+ *
+ *------------------------------------------------------------------------------*/
+
+void ax25_set_ssid (packet_t this_p, int n, int ssid)
+{
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	assert (n >= 0 && n < this_p->num_addr);
+
+	this_p->ssid_etc[n] =   (this_p->ssid_etc[n] & ~ SSID_SSID_MASK) |
+		((ssid << SSID_SSID_SHIFT) & SSID_SSID_MASK) ;
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_get_h
+ * 
+ * Purpose:	Return "has been repeated" flag of specified address in current packet.
+ *
+ * Inputs:	n	- Index of address.   Use the symbols 
+ *			  AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
+ *
+ * Bugs:	No bounds checking is performed.  Be careful.
+ *		  
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	True or false.
+ *
+ *------------------------------------------------------------------------------*/
+
+int ax25_get_h (packet_t this_p, int n)
+{
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	assert (n >= 0 && n < this_p->num_addr);
+
+	return ((this_p->ssid_etc[n] & SSID_H_MASK) >> SSID_H_SHIFT);
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_set_h
+ * 
+ * Purpose:	Set the "has been repeated" flag of specified address in current packet.
+ *
+ * Inputs:	n	- Index of address.   Use the symbols 
+ *			 Should be in range of AX25_REPEATER_1 .. AX25_REPEATER_8.
+ *
+ * Bugs:	No bounds checking is performed.  Be careful.
+ *		  
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	None
+ *
+ *------------------------------------------------------------------------------*/
+
+void ax25_set_h (packet_t this_p, int n)
+{
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	assert (n >= 0 && n < this_p->num_addr);
+
+	this_p->ssid_etc[n] |= SSID_H_MASK;
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_get_heard
+ * 
+ * Purpose:	Return index of the station that we heard.
+ *		
+ * Inputs:	none
+ *
+ *		  
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	If any of the digipeaters have the has-been-repeated bit set, 
+ *		return the index of the last one.  Otherwise return index for source.
+ *
+ *------------------------------------------------------------------------------*/
+
+int ax25_get_heard(packet_t this_p)
+{
+	int i;
+	int result;
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	result = AX25_SOURCE;
+
+	for (i = AX25_REPEATER_1; i < ax25_get_num_addr(this_p); i++) {
+	
+	  if (ax25_get_h(this_p,i)) {
+	    result = i;
+	  }
+	}
+	return (result);
+}
+
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_get_first_not_repeated
+ * 
+ * Purpose:	Return index of the first repeater that does NOT have the 
+ *		"has been repeated" flag set or -1 if none.
+ *
+ * Inputs:	none
+ *
+ *		  
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	In range of X25_REPEATER_1 .. X25_REPEATER_8 or -1 if none.
+ *
+ *------------------------------------------------------------------------------*/
+
+int ax25_get_first_not_repeated(packet_t this_p)
+{
+	int i;
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	for (i = AX25_REPEATER_1; i < ax25_get_num_addr(this_p); i++) {
+	
+	  if ( ! ax25_get_h(this_p,i)) {
+	    return (i);
+	  }
+	}
+	return (-1);
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_get_info
+ * 
+ * Purpose:	Obtain Information part of current packet.
+ *
+ * Inputs:	None.
+ *
+ * Outputs:	paddr	- Starting address is returned here.
+ *
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	Number of octets in the Information part.
+ *		Should be in the range of AX25_MIN_INFO_LEN .. AX25_MAX_INFO_LEN.
+ *
+ *------------------------------------------------------------------------------*/
+
+int ax25_get_info (packet_t this_p, unsigned char **paddr)
+{
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+
+	if (this_p->num_addr >= 2) {
+	  *paddr = this_p->the_rest + ax25_get_info_offset(this_p);
+	  return (ax25_get_num_info(this_p));
+	}
+
+	/* Not AX.25.  Whole packet is info. */
+
+	*paddr = this_p->the_rest;
+	return (this_p->the_rest_len);
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_get_dti
+ * 
+ * Purpose:	Get Data Type Identifier from Information part.
+ *
+ * Inputs:	None.
+ *
+ * Assumption:	ax25_from_text or ax25_from_frame was called first.
+ *
+ * Returns:	First byte from the information part.
+ *
+ *------------------------------------------------------------------------------*/
+
+int ax25_get_dti (packet_t this_p)
+{
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+
+	if (this_p->num_addr >= 2) {
+	  return (this_p->the_rest[ax25_get_info_offset(this_p)]);
+	}
+	return (' ');
+}
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_set_nextp
+ * 
+ * Purpose:	Set next packet object in queue.
+ *
+ * Inputs:	this_p		- Current packet object.
+ *
+ *		next_p		- pointer to next one
+ *
+ * Description:	This is used to build a linked list for a queue.
+ *
+ *------------------------------------------------------------------------------*/
+
+void ax25_set_nextp (packet_t this_p, packet_t next_p)
+{
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	
+	this_p->nextp = next_p;
+}
+
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_get_nextp
+ * 
+ * Purpose:	Obtain next packet object in queue.
+ *
+ * Inputs:	Packet object.
+ *
+ * Returns:	Following object in queue or NULL.
+ *
+ *------------------------------------------------------------------------------*/
+
+packet_t ax25_get_nextp (packet_t this_p)
+{
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	
+	return (this_p->nextp);
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	ax25_format_addrs
+ *
+ * Purpose:	Format all the addresses suitable for printing.
+ *
+ *		The AX.25 spec refers to this as "Source Path Header" - "TNC-2" Format
+ *
+ * Inputs:	Current packet.
+ *		
+ * Outputs:	result	- All addresses combined into a single string of the form:
+ *
+ *				"Source > Destination [ , repeater ... ] :"
+ *
+ *			An asterisk is displayed after the last digipeater 
+ *			with the "H" bit set.  e.g.  If we hear RPT2, 
+ *
+ *			SRC>DST,RPT1,RPT2*,RPT3:
+ *
+ *			No asterisk means the source is being heard directly.
+ *			Needs to be 101 characters to avoid overflowing.
+ *			(Up to 100 characters + \0)
+ *
+ * Errors:	No error checking so caller needs to be careful.
+ *
+ *
+ *------------------------------------------------------------------*/
+
+void ax25_format_addrs (packet_t this_p, char *result)
+{
+	int i;
+	int heard;
+	char stemp[AX25_MAX_ADDR_LEN];
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+	*result = '\0';
+
+	/* New in 0.9. */
+	/* Don't get upset if no addresses.  */
+	/* This will allow packets that do not comply to AX.25 format. */
+
+	if (this_p->num_addr == 0) {
+	  return;
+	}
+
+	ax25_get_addr_with_ssid (this_p, AX25_SOURCE, stemp);
+	strcat (result, stemp);
+	strcat (result, ">");
+
+	ax25_get_addr_with_ssid (this_p, AX25_DESTINATION, stemp);
+	strcat (result, stemp);
+
+	heard = ax25_get_heard(this_p);
+
+	for (i=(int)AX25_REPEATER_1; i<this_p->num_addr; i++) {
+	  ax25_get_addr_with_ssid (this_p, i, stemp);
+	  strcat (result, ",");
+	  strcat (result, stemp);
+	  if (i == heard) {
+	    strcat (result, "*");
+	  }
+	}
+	
+	strcat (result, ":");
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	ax25_pack
+ *
+ * Purpose:	Put all the pieces into format ready for transmission.
+ *
+ * Inputs:	this_p	- pointer to packet object.
+ *		
+ * Outputs:	result		- Frame buffer, AX25_MAX_PACKET_LEN bytes.
+ *				Should also have two extra for FCS to be
+ *				added later.
+ *
+ * Returns:	Number of octets in the frame buffer.  
+ *		Does NOT include the extra 2 for FCS.
+ *
+ * Errors:	Returns -1.
+ *
+ *
+ *------------------------------------------------------------------*/
+
+int ax25_pack (packet_t this_p, unsigned char result[AX25_MAX_PACKET_LEN]) 
+{
+	int j, k;
+	unsigned char *pout;
+	int len;
+
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+
+	pout = result;
+
+	for (j=0; j<this_p->num_addr; j++) {
+
+	  char *s;
+
+	  memset (pout, ' ' << 1, (size_t)6);
+
+	  s = this_p->addrs[j];
+	  for (k=0; *s != '\0'; k++, s++) {
+	    pout[k] = *s << 1;	    
+	  }
+
+	  if (j == this_p->num_addr - 1) {
+	    pout[6] = this_p->ssid_etc[j] | SSID_LAST_MASK;
+	  }
+	  else {
+	   pout[6] = this_p->ssid_etc[j] & ~ SSID_LAST_MASK;
+	  }
+	  pout += 7;
+	}
+
+	memcpy (pout, this_p->the_rest, (size_t)this_p->the_rest_len);
+	pout += this_p->the_rest_len;
+
+	len = pout - result;
+
+	assert (len <= AX25_MAX_PACKET_LEN);
+
+	return (len);
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	ax25_is_aprs
+ *
+ * Purpose:	Is this packet APRS format?
+ *
+ * Inputs:	this_p	- pointer to packet object.
+ *		
+ * Returns:	True if this frame has the proper control
+ *		octets for an APRS packet.
+ *			control		3 for UI frame
+ *			protocol id	0xf0 for no layer 3
+ *
+ *
+ * Description:	Dire Wolf should be able to act as a KISS TNC for
+ *		any type of AX.25 activity.  However, there are other
+ *		places where we want to process only APRS.
+ *		(e.g. digipeating and IGate.)
+ *
+ *------------------------------------------------------------------*/
+
+
+int ax25_is_aprs (packet_t this_p) 
+{
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+
+	return (this_p->num_addr >= 2 &&
+		ax25_get_control(this_p) == AX25_UI_FRAME && 
+		ax25_get_pid(this_p) == AX25_NO_LAYER_3);
+}
+
+/*------------------------------------------------------------------
+ *
+ * Function:	ax25_get_control
+ *
+ * Purpose:	Get Control field from packet.
+ *
+ * Inputs:	this_p	- pointer to packet object.
+ *		
+ * Returns:	APRS uses AX25_UI_FRAME.
+ *		This could also be used in other situations.
+ *
+ *------------------------------------------------------------------*/
+
+
+int ax25_get_control (packet_t this_p) 
+{
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+
+	if (this_p->num_addr >= 2) {
+	  return (this_p->the_rest[ax25_get_control_offset(this_p)]);
+	}
+	return (-1);
+}
+
+/*------------------------------------------------------------------
+ *
+ * Function:	ax25_get_pid
+ *
+ * Purpose:	Get protocol ID from packet.
+ *
+ * Inputs:	this_p	- pointer to packet object.
+ *		
+ * Returns:	APRS uses 0xf0 for no layer 3.
+ *		This could also be used in other situations.
+ *
+ *------------------------------------------------------------------*/
+
+
+int ax25_get_pid (packet_t this_p) 
+{
+	assert (this_p->magic1 == MAGIC);
+	assert (this_p->magic2 == MAGIC);
+
+	if (this_p->num_addr >= 2) {
+	  return (this_p->the_rest[ax25_get_pid_offset(this_p)]);
+	}
+	return (-1);
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_dedupe_crc 
+ * 
+ * Purpose:	Calculate a checksum for the packet source, destination, and
+ *		information but NOT the digipeaters.
+ *		This is used for duplicate detection in the digipeater 
+ *		and IGate algorithms.
+ *
+ * Input:	pp	- Pointer to packet object.
+ *		
+ * Returns:	Value which will be the same for a duplicate but very unlikely 
+ *		to match a non-duplicate packet.
+ *
+ * Description:	For detecting duplicates, we need to look
+ *			+ source station
+ *			+ destination 
+ *			+ information field
+ *		but NOT the changing list of digipeaters.
+ *
+ *		Typically, only a checksum is kept to reduce memory 
+ *		requirements and amount of compution for comparisons.
+ *		There is a very very small probability that two unrelated 
+ *		packets will result in the same checksum, and the
+ *		undesired dropping of the packet.
+ *		
+ *------------------------------------------------------------------------------*/
+
+unsigned short ax25_dedupe_crc (packet_t pp)
+{
+	unsigned short crc;
+	char src[AX25_MAX_ADDR_LEN];
+	char dest[AX25_MAX_ADDR_LEN];
+	unsigned char *pinfo;
+	int info_len;
+
+	ax25_get_addr_with_ssid(pp, AX25_SOURCE, src);
+	ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
+	info_len = ax25_get_info (pp, &pinfo);
+
+	crc = 0xffff;
+	crc = crc16((unsigned char *)src, strlen(src), crc);
+	crc = crc16((unsigned char *)dest, strlen(dest), crc);
+	crc = crc16(pinfo, info_len, crc);
+
+	return (crc);
+}
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	ax25_m_m_crc 
+ * 
+ * Purpose:	Calculate a checksum for the packet.
+ *		This is used for the multimodem duplicate detection.
+ *
+ * Input:	pp	- Pointer to packet object.
+ *		
+ * Returns:	Value which will be the same for a duplicate but very unlikely 
+ *		to match a non-duplicate packet.
+ *
+ * Description:	For detecting duplicates, we need to look the entire packet.
+ *
+ *		Typically, only a checksum is kept to reduce memory 
+ *		requirements and amount of compution for comparisons.
+ *		There is a very very small probability that two unrelated 
+ *		packets will result in the same checksum, and the
+ *		undesired dropping of the packet.
+		
+ *------------------------------------------------------------------------------*/
+
+unsigned short ax25_m_m_crc (packet_t pp)
+{
+	unsigned short crc;
+	unsigned char fbuf[AX25_MAX_PACKET_LEN];
+	int flen;
+
+	flen = ax25_pack (pp, fbuf); 
+
+	crc = 0xffff;
+	crc = crc16(fbuf, flen, crc);
+
+	return (crc);
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	ax25_safe_print
+ *
+ * Purpose:	Print given string, changing non printable characters to 
+ *		hexadecimal notation.   Note that character values
+ *		<DEL>, 28, 29, 30, and 31 can appear in MIC-E message.
+ *
+ * Inputs:	pstr	- Pointer to string.
+ *
+ *		len	- Maximum length if not -1.
+ *
+ *		ascii_only	- Restrict output to only ASCII.
+ *				  Normally we allow UTF-8.
+ *		
+ *		Stops after non-zero len characters or at nul.
+ *
+ * Returns:	none
+ *
+ * Description:	Print a string in a "safe" manner.
+ *		Anything that is not a printable character
+ *		will be converted to a hexadecimal representation.
+ *		For example, a Line Feed character will appear as <0x0a>
+ *		rather than dropping down to the next line on the screen.
+ *
+ *		ax25_from_text can accept this format.
+ *
+ *
+ * Example:	W1MED-1>T2QP0S,N1OHZ,N8VIM*,WIDE1-1:'cQBl <0x1c>-/]<0x0d>
+ *		                                          ------   ------
+ *
+ * Questions:	What should we do about UTF-8?  Should that be displayed
+ *		as hexadecimal for troubleshooting? Maybe an option so the
+ *		packet raw data is in hexadecimal but an extracted 
+ *		comment displays UTF-8?  Or a command line option for only ASCII?
+ *			
+ *------------------------------------------------------------------*/
+
+#define MAXSAFE 500
+
+void ax25_safe_print (char *pstr, int len, int ascii_only)
+{
+	int ch;
+	char safe_str[MAXSAFE*6+1];
+	int safe_len;
+
+	safe_len = 0;
+	safe_str[safe_len] = '\0';
+
+
+	if (len < 0) 
+	  len = strlen(pstr);
+
+	if (len > MAXSAFE)
+	  len = MAXSAFE;
+
+	while (len > 0 && *pstr != '\0')
+	{
+	  ch = *((unsigned char *)pstr);
+
+	  if (ch < ' ' || ch == 0x7f || ch == 0xfe || ch == 0xff ||
+			(ascii_only && ch >= 0x80) ) {
+
+	      /* Control codes and delete. */
+	      /* UTF-8 does not use fe and ff except in a possible */
+	      /* "Byte Order Mark" (BOM) at the beginning. */
+
+	      sprintf (safe_str + safe_len, "<0x%02x>", ch);
+	      safe_len += 6;	      
+	    }
+	  else {
+	    /* Let everything else thru so we can handle UTF-8 */
+	    /* Maybe we should have an option to display 0x80 */
+	    /* and above as hexadecimal. */
+
+	    safe_str[safe_len++] = ch;
+	    safe_str[safe_len] = '\0';
+	  }
+
+	  pstr++;
+	  len--;
+	}
+
+	dw_printf ("%s", safe_str);
+
+} /* end ax25_safe_print */
+
+/* end ax25_pad.c */
+
+
diff --git a/ax25_pad.h b/ax25_pad.h
new file mode 100755
index 0000000..347a394
--- /dev/null
+++ b/ax25_pad.h
@@ -0,0 +1,298 @@
+/*-------------------------------------------------------------------
+ *
+ * Name:	ax25_pad.h
+ *
+ * Purpose:	Header file for using ax25_pad.c
+ *
+ *------------------------------------------------------------------*/
+
+#ifndef AX25_PAD_H
+#define AX25_PAD_H 1
+
+
+#define AX25_MAX_REPEATERS 8
+#define AX25_MIN_ADDRS 2	/* Destinatin & Source. */
+#define AX25_MAX_ADDRS 10	/* Destination, Source, 8 digipeaters. */	
+
+#define AX25_DESTINATION  0	/* Address positions in frame. */
+#define AX25_SOURCE       1	
+#define AX25_REPEATER_1   2
+#define AX25_REPEATER_2   3
+#define AX25_REPEATER_3   4
+#define AX25_REPEATER_4   5
+#define AX25_REPEATER_5   6
+#define AX25_REPEATER_6   7
+#define AX25_REPEATER_7   8
+#define AX25_REPEATER_8   9
+
+#define AX25_MAX_ADDR_LEN 12	/* In theory, you would expect the maximum length */
+				/* to be 6 letters, dash, 2 digits, and nul for a */
+				/* total of 10.  However, object labels can be 10 */
+				/* characters so throw in a couple extra bytes */
+				/* to be safe. */
+
+#define AX25_MIN_INFO_LEN 0	/* Previously 1 when considering only APRS. */
+				
+#define AX25_MAX_INFO_LEN 2048	/* Maximum size for APRS. */
+				/* AX.25 starts out with 256 as the default max */
+				/* length but the end stations can negotiate */
+				/* something different. */
+				/* version 0.8:  Change from 256 to 2028 to */
+				/* handle the larger paclen for Linux AX25. */
+
+				/* These don't include the 2 bytes for the */
+				/* HDLC frame FCS. */
+
+/* 
+ * Previously, for APRS only.
+ * #define AX25_MIN_PACKET_LEN ( 2 * 7 + 2 + AX25_MIN_INFO_LEN)
+ * #define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + AX25_MAX_INFO_LEN)
+ */
+
+/* the more general case. */
+
+#define AX25_MIN_PACKET_LEN ( 2 * 7 + 1 )
+
+#define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + 3 + AX25_MAX_INFO_LEN)
+
+
+/*
+ * packet_t is a pointer to a packet object.
+ *
+ * The actual implementation is not visible outside ax25_pad.c.
+ */
+
+#define AX25_UI_FRAME 3		/* Control field value. */
+#define AX25_NO_LAYER_3 0xf0	/* protocol ID */
+
+
+
+#ifdef AX25_PAD_C	/* Keep this hidden - implementation could change. */
+
+struct packet_s {
+
+	int magic1;		/* for error checking. */
+
+#define MAGIC 0x41583235
+
+	struct packet_s *nextp;	/* Pointer to next in queue. */
+
+	int num_addr;		/* Number of elements used in two below. */
+				/* Range of 0 .. AX25_MAX_ADDRS. */	
+
+	char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
+				/* Contains the address without the ssid. */
+				/* Why is it larger than 7? */
+				/* Messages from an IGate server can have longer */
+				/* addresses after qAC.  Up to 9 observed so far. */
+
+				/* usual human readable form.  e.g.  WB20SZ-15 */
+
+	unsigned char ssid_etc[AX25_MAX_ADDRS];		/*  SSID octet from each address. */
+
+				/* 
+				 * Bits:   H  R  R  SSID  0
+				 *
+				 *   H 		for digipeaters set to 0 intially.
+				 *		Changed to 1 when position has been used.
+ 				 *
+				 *		for source & destination it is called
+				 *		command/response and is normally 1.
+				 *
+				 *   R	R	Reserved.  Normally set to 1 1.
+				 *
+				 *   SSID	Substation ID.  Range of 0 - 15.
+				 *
+				 *   0		Usually 0 but 1 for last address.
+				 */
+
+#define SSID_H_MASK	0x80
+#define SSID_H_SHIFT	7
+
+#define SSID_RR_MASK	0x60
+#define SSID_RR_SHIFT	5
+
+#define SSID_SSID_MASK	0x1e
+#define SSID_SSID_SHIFT	1
+
+#define SSID_LAST_MASK	0x01
+
+
+	int the_rest_len;	/* Frame length minus the address part. */
+
+	unsigned char the_rest[2 + 3 + AX25_MAX_INFO_LEN + 1];
+				/* The rest after removing the addresses. */
+				/* Includes control, protocol ID, Information, */
+				/* and throw in one more for a character */
+				/* string nul terminator. */
+
+	int magic2;		/* Will get stomped on if above overflows. */
+};
+
+
+
+
+#else			/* Public view. */
+
+struct packet_s {
+	int secret;
+};
+
+#endif
+
+
+typedef struct packet_s *packet_t;
+
+
+
+#ifdef AX25_PAD_C	/* Keep this hidden - implementation could change. */
+
+/*
+ * APRS always has one control octet of 0x03 but the more
+ * general AX.25 case is one or two control bytes depending on
+ * "modulo 128 operation" is in effect.  Unfortunately, it seems
+ * this can be determined only by examining the XID frames and 
+ * keeping this information for each connection.
+ * We can assume 1 for our purposes.
+ */
+
+static inline int ax25_get_control_offset (packet_t this_p) 
+{
+	return (0);
+}
+
+static inline int ax25_get_num_control (packet_t this_p)
+{
+	return (1);
+}
+
+
+/*
+ * APRS always has one protocol octet of 0xF0 meaning no level 3
+ * protocol but the more general case is 0, 1 or 2 protocol ID octets.
+ */
+
+static inline int ax25_get_pid_offset (packet_t this_p) 
+{
+	return (ax25_get_num_control(this_p));
+}
+
+static int ax25_get_num_pid (packet_t this_p)
+{
+	int c;
+	int pid;
+
+	c = this_p->the_rest[ax25_get_control_offset(this_p)];
+
+	if ( (c & 0x01) == 0 ||				/* I   xxxx xxx0 */
+	     c == 0x03 || c == 0x13) {			/* UI  000x 0011 */
+
+	  pid = this_p->the_rest[ax25_get_pid_offset(this_p)];
+	  if (pid == 0xff) {
+	    return (2);			/* pid 1111 1111 means another follows. */
+	  }
+	  return (1);		
+	}
+	return (0);
+}
+
+
+/*
+ * APRS always has an Information field with at least one octet for the
+ * Data Type Indicator.  AX.25 has this for only 5 frame types depending
+ * on the control field.
+ *	xxxx xxx0	I
+ *	000x 0011	UI
+ *	101x 1111	XID
+ *	111x 0011	TEST
+ *	100x 0111	FRMR
+ */
+
+static inline int ax25_get_info_offset (packet_t this_p) 
+{
+	return (ax25_get_num_control(this_p) + ax25_get_num_pid(this_p));
+}
+
+static int ax25_get_num_info (packet_t this_p)
+{
+	int len;
+
+	len = this_p->the_rest_len - ax25_get_num_control(this_p) - ax25_get_num_pid(this_p);
+	if (len < 0) {
+	  len = 0;		/* print error? */
+	}
+	return (len);
+}
+
+#endif
+
+
+
+
+
+//static packet_t ax25_new (void);
+
+extern void ax25_delete (packet_t pp);
+
+extern void ax25_clear (packet_t pp);
+
+extern packet_t ax25_from_text (char *, int strict);
+
+extern packet_t ax25_from_frame (unsigned char *data, int len, int alevel);
+
+extern packet_t ax25_dup (packet_t copy_from);
+
+extern int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard);
+
+extern packet_t ax25_unwrap_third_party (packet_t from_pp);
+
+extern void ax25_set_addr (packet_t pp, int, char *);
+extern void ax25_insert_addr (packet_t this_p, int n, char *ad);
+extern void ax25_remove_addr (packet_t this_p, int n);
+
+extern int ax25_get_num_addr (packet_t pp);
+extern int ax25_get_num_repeaters (packet_t this_p);
+
+extern void ax25_get_addr_with_ssid (packet_t pp, int n, char *);
+
+extern int ax25_get_ssid (packet_t pp, int n);
+extern void ax25_set_ssid (packet_t this_p, int n, int ssid);
+
+extern int ax25_get_h (packet_t pp, int n);
+
+extern void ax25_set_h (packet_t pp, int n);
+
+extern int ax25_get_heard(packet_t this_p);
+
+extern int ax25_get_first_not_repeated(packet_t pp);
+
+extern int ax25_get_info (packet_t pp, unsigned char **paddr);
+
+extern void ax25_set_nextp (packet_t this_p, packet_t next_p);
+
+extern int ax25_get_dti (packet_t this_p);
+
+extern packet_t ax25_get_nextp (packet_t this_p);
+
+extern void ax25_format_addrs (packet_t pp, char *);
+
+extern int ax25_pack (packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]);
+
+extern int ax25_is_aprs (packet_t pp);
+
+extern int ax25_get_control (packet_t this_p); 
+
+extern int ax25_get_pid (packet_t this_p);
+
+extern unsigned short ax25_dedupe_crc (packet_t pp);
+
+extern unsigned short ax25_m_m_crc (packet_t pp);
+
+extern void ax25_safe_print (char *, int, int ascii_only);
+
+
+#endif /* AX25_PAD_H */
+
+/* end ax25_pad.h */
+
+
diff --git a/beacon.c b/beacon.c
new file mode 100755
index 0000000..085dcc8
--- /dev/null
+++ b/beacon.c
@@ -0,0 +1,681 @@
+//#define DEBUG 1
+//#define DEBUG_SIM 1
+
+
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2013,2014  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      beacon.c
+ *
+ * Purpose:   	Transmit messages on a fixed schedule.
+ *		
+ * Description:	Transmit periodic messages as specified in the config file.
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+
+#include <time.h>
+#if __WIN32__
+#include <windows.h>
+#endif
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "audio.h"
+#include "tq.h"
+#include "xmit.h"
+#include "config.h"
+#include "digipeater.h"
+#include "version.h"
+#include "encode_aprs.h"
+#include "beacon.h"
+#include "latlong.h"
+#include "dwgps.h"
+
+
+
+/* 
+ * Are we using GPS data?
+ * Incremented if tracker beacons configured.  
+ * Cleared if dwgps_init fails.
+ */
+
+static int g_using_gps = 0;	
+
+/*
+ * Save pointers to configuration settings.
+ */
+
+static struct misc_config_s *g_misc_config_p;
+static struct digi_config_s *g_digi_config_p;
+
+
+
+#if __WIN32__
+static unsigned __stdcall beacon_thread (void *arg);
+#else
+static void * beacon_thread (void *arg);
+#endif
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        beacon_init
+ *
+ * Purpose:     Initialize the beacon process.
+ *
+ * Inputs:	pconfig		- misc. configuration from config file.
+ *		pdigi		- digipeater configuration from config file.
+ *				  Use to obtain "mycall" for each channel.
+ *
+ *
+ * Outputs:	Remember required information for future use.
+ *
+ * Description:	Initialize the queue to be empty and set up other
+ *		mechanisms for sharing it between different threads.
+ *
+ *		Start up xmit_thread to actually send the packets
+ *		at the appropriate time.
+ *
+ *--------------------------------------------------------------------*/
+
+
+
+void beacon_init (struct misc_config_s *pconfig, struct digi_config_s *pdigi)
+{
+	time_t now;
+	int j;
+	int count;
+#if __WIN32__
+	HANDLE beacon_th;
+#else
+	pthread_t beacon_tid;
+#endif
+
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("beacon_init ( ... )\n");
+#endif
+
+
+
+/* 
+ * Save parameters for later use.
+ */
+	g_misc_config_p = pconfig;
+	g_digi_config_p = pdigi;
+
+/*
+ * Precompute the packet contents so any errors are 
+ * Reported once at start up time rather than for each transmission.
+ * If a serious error is found, set type to BEACON_IGNORE and that
+ * table entry should be ignored later on.
+ */
+	for (j=0; j<g_misc_config_p->num_beacons; j++) {
+	  int chan = g_misc_config_p->beacon[j].chan;
+
+	  if (chan < 0) chan = 0;	/* For IGate, use channel 0 call. */
+
+	  if (chan < pdigi->num_chans) {
+
+	    if (strlen(pdigi->mycall[chan]) > 0 && strcasecmp(pdigi->mycall[chan], "NOCALL") != 0) {
+
+              switch (g_misc_config_p->beacon[j].btype) {
+
+	        case BEACON_OBJECT:
+
+		  /* Object name is required. */
+
+		  if (strlen(g_misc_config_p->beacon[j].objname) == 0) {
+	            text_color_set(DW_COLOR_ERROR);
+	            dw_printf ("Config file, line %d: OBJNAME is required for OBEACON.\n", g_misc_config_p->beacon[j].lineno);
+		    g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
+		    continue;
+		  }
+		  /* Fall thru.  Ignore any warning about missing break. */
+
+	        case BEACON_POSITION:
+
+		  /* Location is required. */
+
+		  if (g_misc_config_p->beacon[j].lat == G_UNKNOWN || g_misc_config_p->beacon[j].lon == G_UNKNOWN) {
+	            text_color_set(DW_COLOR_ERROR);
+	            dw_printf ("Config file, line %d: Latitude and longitude are required.\n", g_misc_config_p->beacon[j].lineno);
+		    g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
+		    continue;
+		  }	
+		  break;
+
+	        case BEACON_TRACKER:
+
+#if defined(GPS_ENABLED) || defined(DEBUG_SIM)
+		  g_using_gps++;
+#else
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("Config file, line %d: GPS tracker feature is not enabled.\n", g_misc_config_p->beacon[j].lineno);
+	   	  g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
+	   	  continue;
+#endif
+		  break;
+
+	        case BEACON_CUSTOM:
+
+		  /* INFO is required. */
+
+		  if (g_misc_config_p->beacon[j].custom_info == NULL) {
+	            text_color_set(DW_COLOR_ERROR);
+	            dw_printf ("Config file, line %d: INFO is required for custom beacon.\n", g_misc_config_p->beacon[j].lineno);
+		    g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
+		    continue;
+		  }	
+		  break;
+
+	        case BEACON_IGNORE:
+		  break;
+	      }
+	    }
+	    else {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file, line %d: MYCALL must be set for beacon on channel %d. \n", g_misc_config_p->beacon[j].lineno, chan);
+	      g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
+	    }
+	  }
+	  else {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Config file, line %d: Invalid channel number %d for beacon. \n", g_misc_config_p->beacon[j].lineno, chan);
+	    g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
+	  }
+	}
+
+/*
+ * Calculate next time for each beacon.
+ */
+
+	now = time(NULL);
+
+	for (j=0; j<g_misc_config_p->num_beacons; j++) {
+#if DEBUG
+
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("beacon[%d] chan=%d, delay=%d, every=%d\n",
+		j,
+		g_misc_config_p->beacon[j].chan,
+		g_misc_config_p->beacon[j].delay,
+		g_misc_config_p->beacon[j].every);
+#endif
+	  g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].delay;
+	}
+
+
+/*
+ * Connect to GPS receiver if any tracker beacons are configured.
+ * If open fails, disable all tracker beacons.
+ */
+
+#if DEBUG_SIM
+
+	g_using_gps = 1;
+	
+#elif ENABLE_GPS
+
+	if (g_using_gps > 0) {
+	  int err;
+
+	  err = dwgps_init();
+	  if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("All tracker beacons disabled.\n");
+	    g_using_gps = 0;
+
+	    for (j=0; j<g_misc_config_p->num_beacons; j++) {
+              if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
+		g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
+	      }
+	    }
+	  }
+
+	}
+#endif
+
+
+/* 
+ * Start up thread for processing only if at least one is valid.
+ */
+
+	count = 0;
+	for (j=0; j<g_misc_config_p->num_beacons; j++) {
+          if (g_misc_config_p->beacon[j].btype != BEACON_IGNORE) {
+	    count++;
+	  }
+	}
+
+	if (count >= 1) {
+
+#if __WIN32__
+	  beacon_th = (HANDLE)_beginthreadex (NULL, 0, &beacon_thread, NULL, 0, NULL);
+	  if (beacon_th == NULL) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Could not create beacon thread\n");
+	    return;
+	  }
+#else
+	  int e;
+
+	  e = pthread_create (&beacon_tid, NULL, beacon_thread, (void *)0);
+	  if (e != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    perror("Could not create beacon thread");
+	    return;
+	  }
+#endif
+	}
+
+
+} /* end beacon_init */
+
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        beacon_thread
+ *
+ * Purpose:     Transmit beacons when it is time.
+ *
+ * Inputs:	g_misc_config_p->beacon
+ *
+ * Outputs:	g_misc_config_p->beacon[].next_time
+ *
+ * Description:	Go to sleep until it is time for the next beacon.
+ *		Transmit any beacons scheduled for now.
+ *		Repeat.
+ *
+ *--------------------------------------------------------------------*/
+
+#define KNOTS_TO_MPH 1.150779
+
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
+
+/* Difference between two angles. */
+
+static inline float heading_change (float a, float b)
+{
+	float diff;
+
+	diff = fabs(a - b);
+	if (diff <= 180.)
+	  return (diff);
+	else
+	  return (360. - diff);
+}
+
+
+#if __WIN32__
+static unsigned __stdcall beacon_thread (void *arg)
+#else
+static void * beacon_thread (void *arg)
+#endif
+{
+	int j;
+	time_t earliest;
+	time_t now;
+
+/*
+ * Information from GPS.
+ */
+	int fix = 0;			/* 0 = none, 2 = 2D, 3 = 3D */
+	double my_lat = 0;		/* degrees */
+	double my_lon = 0;
+	float  my_course = 0;		/* degrees */
+	float  my_speed_knots = 0;
+	float  my_speed_mph = 0;
+	float  my_alt = 0;		/* meters */
+
+/*
+ * SmartBeaconing state.
+ */
+	time_t sb_prev_time = 0;	/* Time of most recent transmission. */
+	float sb_prev_course = 0;	/* Most recent course reported. */
+	//float sb_prev_speed_mph;	/* Most recent speed reported. */
+	int sb_every;			/* Calculated time between transmissions. */
+
+
+#if DEBUG
+	struct tm tm;
+	char hms[20];
+
+	now = time(NULL);
+	localtime_r (&now, &tm);
+	strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("beacon_thread: started %s\n", hms);
+#endif
+	now = time(NULL);
+
+	while (1) {
+
+	  assert (g_misc_config_p->num_beacons >= 1);
+
+/* 
+ * Sleep until time for the earliest scheduled or
+ * the soonest we could transmit due to corner pegging.
+ */
+	  
+	  earliest = g_misc_config_p->beacon[0].next;
+	  for (j=1; j<g_misc_config_p->num_beacons; j++) {
+	    if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE)
+	      continue;
+	    earliest = MIN(g_misc_config_p->beacon[j].next, earliest);
+	  }
+
+	  if (g_misc_config_p->sb_configured && g_using_gps) {
+	    earliest = MIN(now + g_misc_config_p->sb_turn_time, earliest);
+            earliest = MIN(now + g_misc_config_p->sb_fast_rate, earliest);
+	  }
+
+	  if (earliest > now) {
+	    SLEEP_SEC (earliest - now);
+	  }
+
+/*
+ * Woke up.  See what needs to be done.
+ */
+	  now = time(NULL);
+
+#if DEBUG
+	  localtime_r (&now, &tm);
+	  strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("beacon_thread: woke up %s\n", hms);
+#endif
+
+/*
+ * Get information from GPS if being used.
+ * This needs to be done before the next scheduled tracker
+ * beacon because corner pegging make it sooner. 
+ */
+
+#if DEBUG_SIM
+	  FILE *fp;
+	  char cs[40];
+
+	  fp = fopen ("c:\\cygwin\\tmp\\cs", "r");
+	  if (fp != NULL) {
+	    fscanf (fp, "%f %f", &my_course, &my_speed_knots);
+	    fclose (fp);
+	  }
+	  else {
+	    fprintf (stderr, "Can't read /tmp/cs.\n");
+	  }
+	  fix = 3;
+	  my_speed_mph = KNOTS_TO_MPH * my_speed_knots;
+	  my_lat = 42.99;
+	  my_lon = 71.99;
+	  my_alt = 100;
+#else
+	  if (g_using_gps) {
+
+	    fix = dwgps_read (&my_lat, &my_lon, &my_speed_knots, &my_course, &my_alt);
+	    my_speed_mph = KNOTS_TO_MPH * my_speed_knots;
+
+	    /* Don't complain here for no fix. */
+	    /* Possibly at the point where about to transmit. */
+	  }
+#endif
+
+/*
+ * Run SmartBeaconing calculation if configured and GPS data available.
+ */
+	  if (g_misc_config_p->sb_configured && g_using_gps && fix >= 2) {
+
+	    if (my_speed_mph > g_misc_config_p->sb_fast_speed) {
+		sb_every = g_misc_config_p->sb_fast_rate;
+	    }
+	    else if (my_speed_mph < g_misc_config_p->sb_slow_speed) {
+	      sb_every = g_misc_config_p->sb_slow_rate;
+	    }
+	    else {
+	      /* Can't divide by 0 assuming sb_slow_speed > 0. */
+	      sb_every = ( g_misc_config_p->sb_fast_rate * g_misc_config_p->sb_fast_speed ) / my_speed_mph;
+	    }
+
+#if DEBUG_SIM
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("SB: fast %d %d slow %d %d speed=%.1f every=%d\n",
+			g_misc_config_p->sb_fast_speed, g_misc_config_p->sb_fast_rate,
+			g_misc_config_p->sb_slow_speed, g_misc_config_p->sb_slow_rate,
+			my_speed_mph, sb_every);
+#endif 
+	
+/*
+ * Test for "Corner Pegging" if moving.
+ */
+	    if (my_speed_mph >= 1.0) {
+	      int turn_threshold = g_misc_config_p->sb_turn_angle + 
+			g_misc_config_p->sb_turn_slope / my_speed_mph;
+
+#if DEBUG_SIM
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("SB-moving: course %.0f  prev %.0f  thresh %d\n",
+		my_course, sb_prev_course, turn_threshold);
+#endif 
+	      if (heading_change(my_course, sb_prev_course) > turn_threshold &&
+		  now >= sb_prev_time + g_misc_config_p->sb_turn_time) {
+
+		/* Send it now. */
+	        for (j=0; j<g_misc_config_p->num_beacons; j++) {
+                  if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
+		    g_misc_config_p->beacon[j].next = now;
+	          }
+	        }
+	      }  /* significant change in direction */
+	    }  /* is moving */
+	  }  /* apply SmartBeaconing */
+	    
+      
+	  for (j=0; j<g_misc_config_p->num_beacons; j++) {
+
+	    if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE)
+	      continue;
+
+	    if (g_misc_config_p->beacon[j].next <= now) {
+
+	      int strict = 1;	/* Strict packet checking because they will go over air. */
+	      char stemp[20];
+	      char info[AX25_MAX_INFO_LEN];
+	      char beacon_text[AX25_MAX_PACKET_LEN];
+	      packet_t pp = NULL;
+	      char mycall[AX25_MAX_ADDR_LEN];
+
+/*
+ * Obtain source call for the beacon.
+ * This could potentially be different on different channels.
+ * When sending to IGate server, use call from first radio channel.
+ *
+ * Check added in version 1.0a.  Previously used index of -1.
+ */
+	      strcpy (mycall, "NOCALL");
+
+	      if (g_misc_config_p->beacon[j].chan == -1) {
+		strcpy (mycall, g_digi_config_p->mycall[0]);
+	      }
+	      else {
+		strcpy (mycall, g_digi_config_p->mycall[g_misc_config_p->beacon[j].chan]);
+	      }
+
+	      if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("MYCALL not set for beacon in config file line %d.\n", g_misc_config_p->beacon[j].lineno);
+		continue;
+	      }
+
+/* 
+ * Prepare the monitor format header. 
+ */
+
+	      strcpy (beacon_text, mycall);
+	      strcat (beacon_text, ">");
+	      sprintf (stemp, "%s%1d%1d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
+	      strcat (beacon_text, stemp);
+	      if (g_misc_config_p->beacon[j].via) {
+	        strcat (beacon_text, ",");
+	        strcat (beacon_text, g_misc_config_p->beacon[j].via);
+	      }
+	      strcat (beacon_text, ":");
+
+/* 
+ * Add the info part depending on beacon type. 
+ */
+	      switch (g_misc_config_p->beacon[j].btype) {
+
+		case BEACON_POSITION:
+
+		  encode_position (g_misc_config_p->beacon[j].compress, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, 
+			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
+			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
+			0, 0, /* course, speed */	
+			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
+			g_misc_config_p->beacon[j].comment,
+			info);
+		  strcat (beacon_text, info);
+	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
+		  break;
+
+		case BEACON_OBJECT:
+
+		  encode_object (g_misc_config_p->beacon[j].objname, g_misc_config_p->beacon[j].compress, 0, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, 
+			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
+			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
+			0, 0, /* course, speed */
+			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, g_misc_config_p->beacon[j].comment,
+			info);
+		  strcat (beacon_text, info);
+	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
+		  break;
+
+		case BEACON_TRACKER:
+
+		  if (fix >= 2) {
+		    int coarse;		/* APRS encoder wants 1 - 360.  */
+					/* 0 means none or unknown. */
+
+		    coarse = (int)roundf(my_course);
+		    if (coarse == 0) {
+		      coarse = 360;
+		    }
+		    encode_position (g_misc_config_p->beacon[j].compress, 
+			my_lat, my_lon, 
+			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
+			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
+			coarse, (int)roundf(my_speed_knots),	
+			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
+			g_misc_config_p->beacon[j].comment,
+			info);
+		    strcat (beacon_text, info);
+
+		    /* Remember most recent tracker beacon. */
+
+		    sb_prev_time = now;
+		    sb_prev_course = my_course;
+		    //sb_prev_speed_mph = my_speed_mph;
+
+		    /* Calculate time for next transmission. */
+	            if (g_misc_config_p->sb_configured) {
+	              g_misc_config_p->beacon[j].next = now + sb_every;
+	            }
+	            else {
+	              g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
+	            }
+	 	  }
+	          else {
+		    g_misc_config_p->beacon[j].next = now + 2;
+	            continue;   /* No fix.  Try again in a couple seconds. */
+		  }
+		  break;
+
+		case BEACON_CUSTOM:
+
+		  if (g_misc_config_p->beacon[j].custom_info != NULL) {
+	            strcat (beacon_text, g_misc_config_p->beacon[j].custom_info);
+		  }
+		  else {
+		    text_color_set(DW_COLOR_ERROR);
+	    	    dw_printf ("Internal error. custom_info is null. %s %d\n", __FILE__, __LINE__);
+	            continue;
+	          }
+	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
+		  break;
+
+		case BEACON_IGNORE:		
+	        default:
+		  break;
+
+	      } /* switch beacon type. */
+
+/*
+ * Parse monitor format into form for transmission.
+ */	
+	      pp = ax25_from_text (beacon_text, strict);
+
+              if (pp != NULL) {
+
+		/* Send to IGate server or radio. */
+
+	        if (g_misc_config_p->beacon[j].chan == -1) {
+#if 1
+	  	  text_color_set(DW_COLOR_XMIT);
+	  	  dw_printf ("[ig] %s\n", beacon_text);
+#endif
+		  igate_send_rec_packet (0, pp);
+		  ax25_delete (pp);
+	 	}
+		else {
+	          tq_append (g_misc_config_p->beacon[j].chan, TQ_PRIO_1_LO, pp);
+		}
+	      }
+	      else {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Config file: Failed to parse packet constructed from line %d.\n", g_misc_config_p->beacon[j].lineno);
+	        dw_printf ("%s\n", beacon_text);
+	      }
+
+	    }  /* if time to send it */
+
+	  }  /* for each configured beacon */
+
+	}  /* do forever */
+
+} /* end beacon_thread */
+
+/* end beacon.c */
diff --git a/beacon.h b/beacon.h
new file mode 100755
index 0000000..97f3dd5
--- /dev/null
+++ b/beacon.h
@@ -0,0 +1,4 @@
+
+/* beacon.h */
+
+void beacon_init (struct misc_config_s *pconfig, struct digi_config_s *pdigi);
diff --git a/config.c b/config.c
new file mode 100755
index 0000000..d3f7eda
--- /dev/null
+++ b/config.c
@@ -0,0 +1,2462 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011, 2012, 2013, 2014  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+// #define DEBUG 1
+
+/*------------------------------------------------------------------
+ *
+ * Module:      config.c
+ *
+ * Purpose:   	Read configuration information from a file.
+ *		
+ * Description:	This started out as a simple little application with a few
+ *		command line options.  Due to creeping featurism, it's now
+ *		time to add a configuration file to specify options.
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+
+#if __WIN32__
+#include "pthreads/pthread.h"
+#else
+#include <pthread.h>
+#endif
+
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "audio.h"
+#include "digipeater.h"
+#include "config.h"
+#include "aprs_tt.h"
+#include "igate.h"
+#include "latlong.h"
+#include "symbols.h"
+
+
+//#include "tq.h"
+
+/* 
+ * Conversions from various units to meters.
+ * There is some disagreement about the exact values for some of these. 
+ * Close enough for our purposes.
+ * Parsec, light year, and angstrom are probably not useful.
+ */
+
+static const struct units_s {
+	char *name;
+	float meters;
+} units[] = {
+	{	"barleycorn",	0.008466667	},	
+	{	"inch",		0.0254		},
+	{	"in",		0.0254		},
+	{	"hand",		0.1016		},	
+	{	"shaku",	0.3030		},	
+	{	"foot",		0.304801	},	
+	{	"ft",		0.304801	},	
+	{	"cubit",	0.4572		},	
+	{	"megalithicyard", 0.8296	},	
+	{	"my",		0.8296		},	
+	{	"yard",		0.914402	},
+	{	"yd",		0.914402	},
+	{	"m",		1.		},	
+	{	"meter",	1.		},	
+	{	"metre",	1.		},	
+	{	"ell",		1.143		},	
+	{	"ken",		1.818		},	
+	{	"hiro",		1.818		},	
+	{	"fathom",	1.8288		},	
+	{	"fath",		1.8288		},	
+	{	"toise",	1.949		},
+	{	"jo",		3.030		},
+	{	"twain",	3.6576074	},	
+	{	"rod",		5.0292		},	
+	{	"rd",		5.0292		},	
+	{	"perch",	5.0292		},	
+	{	"pole",		5.0292		},	
+	{	"rope",		6.096		},	
+	{	"dekameter",	10.		},	
+	{	"dekametre",	10.		},	
+	{	"dam",		10.		},	
+	{	"chain",	20.1168		},
+	{	"ch",		20.1168		},
+	{	"actus",	35.47872	},	
+	{	"arpent",	58.471		},	
+	{	"hectometer",	100.		},	
+	{	"hectometre",	100.		},	
+	{	"hm",		100.		},	
+	{	"cho",		109.1		},	
+	{	"furlong",	201.168		},
+	{	"fur",		201.168		},
+	{	"kilometer",	1000.		},	
+	{	"kilometre",	1000.		},	
+	{	"km",		1000.		},	
+	{	"mile",		1609.344	},	
+	{	"mi",		1609.344	},	
+	{	"ri",		3927.		},	
+	{	"league",	4828.032	},	
+	{	"lea",		4828.032	} };
+
+#define NUM_UNITS (sizeof(units) / sizeof(struct units_s))
+
+static int beacon_options(char *cmd, struct beacon_s *b, int line);
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        parse_ll
+ *
+ * Purpose:     Parse latitude or longitude from configuration file.
+ *
+ * Inputs:      str	- String like [-]deg[^min][hemisphere]
+ *
+ *		which	- LAT or LON for error checking and message.
+ *
+ *		line	- Line number for use in error message. 
+ *
+ * Returns:     Coordinate in signed degrees.
+ *
+ *----------------------------------------------------------------*/
+
+/* Acceptable symbols to separate degrees & minutes. */
+/* Degree symbol is not in ASCII so documentation says to use "^" instead. */
+/* Some wise guy will try to use degree symbol. */
+
+#define DEG1 '^'
+#define DEG2 0xb0	/* ISO Latin1 */
+#define DEG3 0xf8	/* Microsoft code page 437 */
+
+// TODO: recognize UTF-8 degree symbol.
+
+
+enum parse_ll_which_e { LAT, LON };
+
+static double parse_ll (char *str, enum parse_ll_which_e which, int line)
+{
+	char stemp[40];
+	int sign;
+	double degrees, minutes;
+	char *endptr;
+	char hemi;
+	int limit;
+	unsigned char sep;
+
+/*
+ * Remove any negative sign.
+ */
+	strcpy (stemp, str);
+	sign = +1;
+	if (stemp[0] == '-') {
+	  sign = -1;
+	  stemp[0] = ' ';
+	}
+/*
+ * Process any hemisphere on the end.
+ */
+	if (strlen(stemp) >= 2) {
+	  endptr = stemp + strlen(stemp) - 1;
+	  if (isalpha(*endptr)) {
+
+	    hemi = *endptr;
+	    *endptr = '\0';
+	    if (islower(hemi)) {
+	      hemi = toupper(hemi);
+	    }
+
+	    if (hemi == 'W' || hemi == 'S') {
+	      sign = -sign;
+	    }
+
+	    if (which == LAT) {
+	      if (hemi != 'N' && hemi != 'S') {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: Latitude hemisphere in \"%s\" is not N or S.\n", line, str);
+	      }
+	    }
+	    else {
+	      if (hemi != 'E' && hemi != 'W') {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: Longitude hemisphere in \"%s\" is not E or W.\n", line, str);
+	      }
+	    }
+	  }
+	}
+
+/*
+ * Parse the degrees part.
+ */
+	degrees = strtod (stemp, &endptr);
+
+/*
+ * Is there a minutes part?
+ */
+	sep = *endptr;
+	if (sep != '\0') {
+
+	  if (sep == DEG1 || sep == DEG2 || sep == DEG3) {
+	 
+	    minutes = strtod (endptr+1, &endptr);
+	    if (*endptr != '\0') {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Unexpected character '%c' in location \"%s\"\n", line, sep, str);
+	    }
+	    if (minutes >= 60.0) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Number of minutes in \"%s\" is >= 60.\n", line, str);
+	    }
+	    degrees += minutes / 60.0;
+	  }
+	  else {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Line %d: Unexpected character '%c' in location \"%s\"\n", line, sep, str);
+	  }
+	}
+
+	degrees = degrees * sign;
+
+	limit = which == LAT ? 90 : 180;
+	if (degrees < -limit || degrees > limit) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Line %d: Number of degrees in \"%s\" is out of range for %s\n", line, str,
+		which == LAT ? "latitude" : "longitude");
+	}
+	//dw_printf ("%s = %f\n", str, degrees);
+	return (degrees);
+}
+
+#if 0
+main ()
+{
+
+	parse_ll ("12.5", LAT);
+	parse_ll ("12.5N", LAT);
+	parse_ll ("12.5E", LAT);	// error
+
+	parse_ll ("-12.5", LAT);
+	parse_ll ("12.5S", LAT);
+	parse_ll ("12.5W", LAT);	// error
+
+	parse_ll ("12.5", LON);
+	parse_ll ("12.5E", LON);
+	parse_ll ("12.5N", LON);	// error
+
+	parse_ll ("-12.5", LON);
+	parse_ll ("12.5W", LON);
+	parse_ll ("12.5S", LON);	// error
+
+	parse_ll ("12^30", LAT);
+	parse_ll ("12�30", LAT);
+
+	parse_ll ("91", LAT);		// out of range
+	parse_ll ("91", LON);
+	parse_ll ("181", LON);		// out of range
+
+	parse_ll ("12&5", LAT);		// bad character
+}
+#endif
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        parse_interval
+ *
+ * Purpose:     Parse time interval from configuration file.
+ *
+ * Inputs:      str	- String like 10 or 9:30
+ *
+ *		line	- Line number for use in error message. 
+ *
+ * Returns:     Number of seconds.
+ *
+ * Description:	This is used by the BEACON configuration items
+ *		for initial delay or time between beacons.
+ *
+ *		The format is either minutes or minutes:seconds.
+ *
+ *----------------------------------------------------------------*/
+
+
+static int parse_interval (char *str, int line)
+{
+	char *p;
+	int sec;
+	int nc = 0;
+	int bad = 0;
+
+	for (p = str; *p != '\0'; p++) {
+	  if (*p == ':') nc++;
+	  else if ( ! isdigit(*p)) bad++;
+	}
+	if (bad > 0 || nc > 1) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Config file, line %d: Time interval must be of the form minutes or minutes:seconds.\n", line);
+	}
+
+	p = strchr (str, ':');
+
+	if (p != NULL) {
+	  sec = atoi(str) * 60 + atoi(p+1);
+	}
+	else {
+	  sec = atoi(str) * 60;
+	}
+
+	return (sec);
+
+} /* end parse_interval */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        config_init
+ *
+ * Purpose:     Read configuration file when application starts up.
+ *
+ * Inputs:	fname		- Name of configuration file.
+ *
+ * Outputs:	p_modem		- Radio channel parameters stored here.
+ *
+ *		p_digi_config	- Digipeater configuration stored here.
+ *
+ *		p_tt_config	- APRStt stuff.
+ *
+ *		p_igate_config	- Internet Gateway.
+ *	
+ *		p_misc_config	- Everything else.  This wasn't thought out well.
+ *
+ * Description:	Apply default values for various parameters then read the 
+ *		the configuration file which can override those values.
+ *
+ * Errors:	For invalid input, display line number and message on stdout (not stderr).
+ *		In many cases this will result in keeping the default rather than aborting.
+ *
+ * Bugs:	Very simple-minded parsing.
+ *		Not much error checking.  (e.g. atoi() will return 0 for invalid string.)
+ *		Not very forgiving about sloppy input.
+ *
+ *--------------------------------------------------------------------*/
+
+
+void config_init (char *fname, struct audio_s *p_modem, 
+			struct digi_config_s *p_digi_config,
+			struct tt_config_s *p_tt_config,
+			struct igate_config_s *p_igate_config,
+			struct misc_config_s *p_misc_config)
+{
+	FILE *fp;
+	char stuff[256];
+	//char *p;
+	//int c, p;
+	//int err;
+	int line;
+	int channel;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("config_init ( %s )\n", fname);
+#endif
+
+/* 
+ * First apply defaults.
+ */
+
+	memset (p_modem, 0, sizeof(struct audio_s));
+
+	strcpy (p_modem->adevice_in, DEFAULT_ADEVICE);
+	strcpy (p_modem->adevice_out, DEFAULT_ADEVICE);
+
+	p_modem->num_channels = DEFAULT_NUM_CHANNELS;		/* -2 stereo */
+	p_modem->samples_per_sec = DEFAULT_SAMPLES_PER_SEC;	/* -r option */
+	p_modem->bits_per_sample = DEFAULT_BITS_PER_SAMPLE;	/* -8 option for 8 instead of 16 bits */
+	p_modem->fix_bits = DEFAULT_FIX_BITS;
+
+	for (channel=0; channel<MAX_CHANS; channel++) {
+
+	  p_modem->modem_type[channel] = AFSK;			
+	  p_modem->mark_freq[channel] = DEFAULT_MARK_FREQ;		/* -m option */
+	  p_modem->space_freq[channel] = DEFAULT_SPACE_FREQ;		/* -s option */
+	  p_modem->baud[channel] = DEFAULT_BAUD;			/* -b option */
+
+	  /* None.  Will set default later based on other factors. */
+	  strcpy (p_modem->profiles[channel], "");	
+
+	  p_modem->num_freq[channel] = 1;				
+	  p_modem->num_subchan[channel] = 1;				
+	  p_modem->offset[channel] = 0;
+
+	  //  temp test.
+	  // p_modem->num_subchan[channel] = 9;				
+	  // p_modem->offset[channel] = 60;
+
+	  p_modem->ptt_method[channel] = PTT_METHOD_NONE;
+	  strcpy (p_modem->ptt_device[channel], "");
+	  p_modem->ptt_line[channel] = PTT_LINE_RTS;
+	  p_modem->ptt_gpio[channel] = 0;
+	  p_modem->ptt_invert[channel] = 0;
+
+	  p_modem->slottime[channel] = DEFAULT_SLOTTIME;				
+	  p_modem->persist[channel] = DEFAULT_PERSIST;				
+	  p_modem->txdelay[channel] = DEFAULT_TXDELAY;				
+	  p_modem->txtail[channel] = DEFAULT_TXTAIL;				
+	}
+
+	memset (p_digi_config, 0, sizeof(struct digi_config_s));
+	p_digi_config->num_chans = p_modem->num_channels;
+	p_digi_config->dedupe_time = DEFAULT_DEDUPE;
+
+	memset (p_tt_config, 0, sizeof(struct tt_config_s));	
+	p_tt_config->ttloc_size = 2;	/* Start with at least 2.  */
+					/* When full, it will be increased by 50 %. */
+	p_tt_config->ttloc_ptr = malloc (sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
+	p_tt_config->ttloc_len = 0;
+
+	/* Retention time and decay algorithm from 13 Feb 13 version of */
+	/* http://www.aprs.org/aprstt/aprstt-coding24.txt */
+
+	p_tt_config->retain_time = 80 * 60;
+	p_tt_config->num_xmits = 7;
+	assert (p_tt_config->num_xmits <= TT_MAX_XMITS);
+	p_tt_config->xmit_delay[0] = 3;		/* Before initial transmission. */
+	p_tt_config->xmit_delay[1] = 16;
+	p_tt_config->xmit_delay[2] = 32;
+	p_tt_config->xmit_delay[3] = 64;
+	p_tt_config->xmit_delay[4] = 2 * 60;
+	p_tt_config->xmit_delay[5] = 4 * 60;
+	p_tt_config->xmit_delay[6] = 8 * 60;
+
+	memset (p_misc_config, 0, sizeof(struct misc_config_s));
+	p_misc_config->num_channels = p_modem->num_channels;
+	p_misc_config->agwpe_port = DEFAULT_AGWPE_PORT;
+	p_misc_config->kiss_port = DEFAULT_KISS_PORT;
+	p_misc_config->enable_kiss_pt = 0;				/* -p option */
+
+	/* Defaults from http://info.aprs.net/index.php?title=SmartBeaconing */
+
+	p_misc_config->sb_configured = 0;	/* TRUE if SmartBeaconing is configured. */
+	p_misc_config->sb_fast_speed = 60;	/* MPH */
+	p_misc_config->sb_fast_rate = 180;	/* seconds */
+	p_misc_config->sb_slow_speed = 5;	/* MPH */
+	p_misc_config->sb_slow_rate = 1800;	/* seconds */
+	p_misc_config->sb_turn_time = 15;	/* seconds */
+	p_misc_config->sb_turn_angle = 30;	/* degrees */
+	p_misc_config->sb_turn_slope = 255;	/* degrees * MPH */
+
+	memset (p_igate_config, 0, sizeof(struct igate_config_s));
+	p_igate_config->t2_server_port = DEFAULT_IGATE_PORT;
+	p_igate_config->tx_chan = -1;			/* IS->RF not enabled */
+	p_igate_config->tx_limit_1 = 6;
+	p_igate_config->tx_limit_5 = 20;
+
+
+	/* People find this confusing. */
+	/* Ideally we'd like to figure out if com0com is installed */
+	/* and automatically enable this.  */
+	
+	//strcpy (p_misc_config->nullmodem, DEFAULT_NULLMODEM);
+	strcpy (p_misc_config->nullmodem, "");
+
+
+/* 
+ * Try to extract options from a file.
+ * 
+ * Windows:  File must be in current working directory.
+ *
+ * Linux: Search current directory then home directory.
+ */
+
+
+	channel = 0;
+
+	fp = fopen (fname, "r");
+#ifndef __WIN32__
+	if (fp == NULL && strcmp(fname, "direwolf.conf") == 0) {
+	/* Failed to open the default location.  Try home dir. */
+	  char *p;
+
+	  p = getenv("HOME");
+	  if (p != NULL) {
+	    strcpy (stuff, p);
+	    strcat (stuff, "/direwolf.conf");
+	    fp = fopen (stuff, "r");
+	  } 
+	}
+#endif
+	if (fp == NULL)	{
+	  // TODO: not exactly right for all situations.
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("ERROR - Could not open config file %s\n", fname);
+	  dw_printf ("Try using -c command line option for alternate location.\n");
+	  return;
+	}
+	
+
+	line = 0;
+	while (fgets(stuff, sizeof(stuff), fp) != NULL) {
+	  char *t;
+
+	  line++;
+
+
+	  t = strtok (stuff, " ,\t\n\r");
+	  if (t == NULL) {
+	    continue;
+	  }
+
+	  if (*t == '#' || *t == '*') {
+	    continue;
+	  }
+
+
+
+/*
+ * ==================== Audio device parameters ==================== 
+ */
+
+/*
+ * ADEVICE 		- Name of input sound device, and optionally output, if different.
+ */
+
+	  /* Note that ALSA name can contain comma such as hw:1,0 */
+
+	  if (strcasecmp(t, "ADEVICE") == 0) {
+	    t = strtok (NULL, " \t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Missing name of audio device for ADEVICE command on line %d.\n", line);
+	      continue;
+	    }
+	    strncpy (p_modem->adevice_in, t, sizeof(p_modem->adevice_in)-1);
+	    strncpy (p_modem->adevice_out, t, sizeof(p_modem->adevice_out)-1);
+
+	    t = strtok (NULL, " \t\n\r");
+	    if (t != NULL) {
+	      strncpy (p_modem->adevice_out, t, sizeof(p_modem->adevice_out)-1);
+	    }
+	  }
+
+/*
+ * ARATE 		- Audio samples per second, 11025, 22050, 44100, etc.
+ */
+
+	  else if (strcasecmp(t, "ARATE") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing audio sample rate for ARATE command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= MIN_SAMPLES_PER_SEC && n <= MAX_SAMPLES_PER_SEC) {
+	      p_modem->samples_per_sec = n;
+	    }
+	    else {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Use a more reasonable audio sample rate in range of %d - %d.\n", 
+							line, MIN_SAMPLES_PER_SEC, MAX_SAMPLES_PER_SEC);
+   	    }
+	  }
+
+/*
+ * ACHANNELS 		- Number of audio channels: 1 or 2
+ */
+
+	  else if (strcasecmp(t, "ACHANNELS") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing number of audio channels for ACHANNELS command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 1 && n <= MAX_CHANS) {
+	      p_modem->num_channels = n;
+	      p_digi_config->num_chans = p_modem->num_channels;
+	      p_misc_config->num_channels = p_modem->num_channels;
+	    }
+	    else {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Number of audio channels must be 1 or 2.\n", line);
+   	    }
+	  }
+
+/*
+ * ==================== Radio channel parameters ==================== 
+ */
+
+/*
+ * CHANNEL 		- Set channel for following commands.
+ */
+
+	  else if (strcasecmp(t, "CHANNEL") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing channel number for CHANNEL command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 0 && n < MAX_CHANS) {
+	      channel = n;
+	    }
+	    else {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Audio channel number must be 0 or 1.\n", line);
+	      channel = 0;
+   	    }
+	  }
+
+/*
+ * MYCALL station
+ */
+	  else if (strcasecmp(t, "mycall") == 0) {
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Missing value for MYCALL command on line %d.\n", line);
+	      continue;
+	    }
+	    else {
+	      char *p;
+
+	      strncpy (p_digi_config->mycall[channel], t, sizeof(p_digi_config->mycall[channel])-1);
+
+	      for (p = p_digi_config->mycall[channel]; *p != '\0'; p++) {
+	        if (islower(*p)) {
+		  *p = toupper(*p);	/* silently force upper case. */
+	        }
+	      }
+	      // TODO: additional checks if valid
+	    }
+	  }
+
+
+/*
+ * MODEM	- Replaces former HBAUD, MARK, SPACE, and adds new multi modem capability.
+ *
+ * MODEM  baud [ mark  space  [A][B][C]  [  num-decoders spacing ] ]
+ */
+
+	  else if (strcasecmp(t, "MODEM") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing date transmission rate for MODEM command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 100 && n <= 10000) {
+	      p_modem->baud[channel] = n;
+	      if (n != 300 && n != 1200 && n != 9600) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: Warning: Non-standard baud rate.  Are you sure?\n", line);
+    	      }
+	    }
+	    else {
+	      p_modem->baud[channel] = DEFAULT_BAUD;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Unreasonable baud rate. Using %d.\n", 
+				 line, p_modem->baud[channel]);
+   	    }
+
+
+
+	    /* Get mark frequency. */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_INFO);
+	      dw_printf ("Note: Using scrambled baseband rather than AFSK modem.\n");
+	      p_modem->modem_type[channel] = SCRAMBLE;	
+	      p_modem->mark_freq[channel] = 0;	
+	      p_modem->space_freq[channel] = 0;	
+	      continue;
+	    }
+
+	    n = atoi(t);
+	    /* Originally the upper limit was 3000. */
+	    /* Version 1.0 increased to 5000 because someone */
+	    /* wanted to use 2400/4800 Hz AFSK. */
+	    /* Of course the MIC and SPKR connections won't */
+	    /* have enough bandwidth so radios must be modified. */
+            if (n >= 300 && n <= 5000) {
+	      p_modem->mark_freq[channel] = n;
+	    }
+	    else {
+	      p_modem->mark_freq[channel] = DEFAULT_MARK_FREQ;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Unreasonable mark tone frequency. Using %d.\n", 
+				 line, p_modem->mark_freq[channel]);
+   	    }
+	    
+	    /* Get space frequency */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing tone frequency for space.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 300 && n <= 5000) {
+	      p_modem->space_freq[channel] = n;
+	    }
+	    else {
+	      p_modem->space_freq[channel] = DEFAULT_SPACE_FREQ;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Unreasonable space tone frequency. Using %d.\n", 
+					 line, p_modem->space_freq[channel]);
+   	    }
+
+	    /* New feature in 0.9 - Optional filter profile(s). */
+
+	    /* First, set a default based on platform and baud. */
+
+	    if (p_modem->baud[channel] < 600) {
+
+	      /* "D" is a little better at 300 baud. */
+
+	      strcpy (p_modem->profiles[channel], "D");
+	    }
+	    else {
+#if __arm__
+	      /* We probably don't have a lot of CPU power available. */
+
+	      if (p_modem->baud[channel] == DEFAULT_BAUD &&
+		  p_modem->mark_freq[channel] == DEFAULT_MARK_FREQ && 
+		  p_modem->space_freq[channel] == DEFAULT_SPACE_FREQ &&
+		  p_modem->samples_per_sec == DEFAULT_SAMPLES_PER_SEC) {
+
+	        strcpy (p_modem->profiles[channel], "F");
+	      }
+	      else {
+	        strcpy (p_modem->profiles[channel], "A");
+	      }
+#else
+	      strcpy (p_modem->profiles[channel], "C");
+#endif
+	    }
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t != NULL) {
+	      if (isalpha(t[0])) {
+		// TODO: should check all letters.
+		strncpy (p_modem->profiles[channel], t, sizeof(p_modem->profiles[channel]));
+	        p_modem->num_subchan[channel] = strlen(p_modem->profiles[channel]);
+	        t = strtok (NULL, " ,\t\n\r");
+		if (strlen(p_modem->profiles[channel]) > 1 && t != NULL) {
+	          text_color_set(DW_COLOR_ERROR);
+                  dw_printf ("Line %d: Can't combine multiple demodulator types and multiple frequencies.\n", line);
+		  continue;
+		}
+	      }
+	    }
+
+	    /* New feature in 0.9 - optional number of decoders and frequency offset between. */
+
+	    if (t != NULL) {
+	      n = atoi(t);
+              if (n < 1 || n > MAX_SUBCHANS) {
+	        text_color_set(DW_COLOR_ERROR);
+                dw_printf ("Line %d: Number of modems is out of range. Using 3.\n", line);
+		n = 3;
+	      }
+	      p_modem->num_freq[channel] = n;
+	      p_modem->num_subchan[channel] = n;
+
+	      t = strtok (NULL, " ,\t\n\r");
+	      if (t != NULL) {
+	        n = atoi(t);
+                if (n < 5 || n > abs(p_modem->mark_freq[channel] - p_modem->space_freq[channel])/2) {
+	          text_color_set(DW_COLOR_ERROR);
+                  dw_printf ("Line %d: Unreasonable value for offset between modems.  Using 50 Hz.\n", line);
+		  n = 50;
+	        }
+		p_modem->offset[channel] = n;
+	      }
+	      else {
+	        text_color_set(DW_COLOR_ERROR);
+                dw_printf ("Line %d: Missing frequency offset between modems.  Using 50 Hz.\n", line);
+	        p_modem->offset[channel] = 50;
+	      }
+
+// TODO: power saver	      
+	    }
+	  }
+
+/*
+ * (deprecated) HBAUD 		- Set data bits per second.  Standard values are 300 & 1200 for AFSK
+ *				and 9600 for baseband with scrambling.
+ */
+
+	  else if (strcasecmp(t, "HBAUD") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing date transmission rate for HBAUD command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 100 && n <= 10000) {
+	      p_modem->baud[channel] = n;
+	      if (n != 300 && n != 1200 && n != 9600) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: Warning: Non-standard baud rate.  Are you sure?\n", line);
+    	      }
+	      if (n == 9600) {
+		/* TODO: should be separate option to keep it more general. */
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: Note: Using scrambled baseband for 9600 baud.\n", line);
+	        p_modem->modem_type[channel] = SCRAMBLE;		
+    	      }
+	    }
+	    else {
+	      p_modem->baud[channel] = DEFAULT_BAUD;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Unreasonable baud rate. Using %d.\n", 
+				 line, p_modem->baud[channel]);
+   	    }
+	  }
+
+/*
+ * (deprecated) MARK 		- Mark tone frequency.
+ */
+
+	  else if (strcasecmp(t, "MARK") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing tone frequency for MARK command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 300 && n <= 3000) {
+	      p_modem->mark_freq[channel] = n;
+	    }
+	    else {
+	      p_modem->mark_freq[channel] = DEFAULT_MARK_FREQ;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Unreasonable mark tone frequency. Using %d.\n", 
+				 line, p_modem->mark_freq[channel]);
+   	    }
+	  }
+
+/*
+ * (deprecated) SPACE 		- Space tone frequency.
+ */
+
+	  else if (strcasecmp(t, "SPACE") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing tone frequency for SPACE command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 300 && n <= 3000) {
+	      p_modem->space_freq[channel] = n;
+	    }
+	    else {
+	      p_modem->space_freq[channel] = DEFAULT_SPACE_FREQ;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Unreasonable space tone frequency. Using %d.\n", 
+					 line, p_modem->space_freq[channel]);
+   	    }
+	  }
+
+/*
+ * PTT 		- Push To Talk signal line.
+ *
+ * PTT  serial-port [-]rts-or-dtr
+ * PTT  GPIO  [-]gpio-num
+ */
+
+	  else if (strcasecmp(t, "PTT") == 0) {
+	    //int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file line %d: Missing serial port name for PTT command.\n", 
+			line);
+	      continue;
+	    }
+
+	    if (strcasecmp(t, "GPIO") != 0) {
+
+/* serial port case. */
+
+	      strncpy (p_modem->ptt_device[channel], t, sizeof(p_modem->ptt_device[channel]));
+
+	      t = strtok (NULL, " ,\t\n\r");
+	      if (t == NULL) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Config file line %d: Missing RTS or DTR after PTT device name.\n", 
+			line);
+	        continue;
+	      }
+
+	      if (strcasecmp(t, "rts") == 0) {
+	        p_modem->ptt_line[channel] = PTT_LINE_RTS;
+		p_modem->ptt_invert[channel] = 0;
+	      }
+	      else if (strcasecmp(t, "dtr") == 0) {
+	        p_modem->ptt_line[channel] = PTT_LINE_DTR;
+		p_modem->ptt_invert[channel] = 0;
+	      }
+	      else if (strcasecmp(t, "-rts") == 0) {
+	        p_modem->ptt_line[channel] = PTT_LINE_RTS;
+		p_modem->ptt_invert[channel] = 1;
+	      }
+	      else if (strcasecmp(t, "-dtr") == 0) {
+	        p_modem->ptt_line[channel] = PTT_LINE_DTR;
+		p_modem->ptt_invert[channel] = 1;
+	      }
+	      else {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Config file line %d: Expected RTS or DTR after PTT device name.\n", 
+			line);
+	        continue;
+	      }
+
+	      p_modem->ptt_method[channel] = PTT_METHOD_SERIAL;
+	    }
+	    else {
+
+/* GPIO case, Linux only. */
+
+// TODO:
+#if 0
+//#if __WIN32__
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file line %d: PTT with GPIO is only available on Linux.\n", line);
+#else		
+	      t = strtok (NULL, " ,\t\n\r");
+	      if (t == NULL) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Config file line %d: Missing GPIO number.\n", line);
+	        continue;
+	      }
+
+	      if (*t == '-') {
+	        p_modem->ptt_gpio[channel] = atoi(t+1);
+		p_modem->ptt_invert[channel] = 1;
+	      }
+	      else {
+	        p_modem->ptt_gpio[channel] = atoi(t);
+		p_modem->ptt_invert[channel] = 0;
+	      }
+	      p_modem->ptt_method[channel] = PTT_METHOD_GPIO;
+#endif
+	    }
+	  }
+
+
+/*
+ * SLOTTIME 		- For non-digipeat transmit delay timing.
+ */
+
+	  else if (strcasecmp(t, "SLOTTIME") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing delay time for SLOTTIME command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 0 && n <= 255) {
+	      p_modem->slottime[channel] = n;
+	    }
+	    else {
+	      p_modem->slottime[channel] = DEFAULT_SLOTTIME;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Invalid delay time for persist algorithm. Using %d.\n", 
+			line, p_modem->slottime[channel]);
+   	    }
+	  }
+
+/*
+ * PERSIST 		- For non-digipeat transmit delay timing.
+ */
+
+	  else if (strcasecmp(t, "PERSIST") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing probability for PERSIST command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 0 && n <= 255) {
+	      p_modem->persist[channel] = n;
+	    }
+	    else {
+	      p_modem->persist[channel] = DEFAULT_PERSIST;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Invalid probability for persist algorithm. Using %d.\n", 
+			line, p_modem->persist[channel]);
+   	    }
+	  }
+
+/*
+ * TXDELAY 		- For transmit delay timing.
+ */
+
+	  else if (strcasecmp(t, "TXDELAY") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing time for TXDELAY command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 0 && n <= 255) {
+	      p_modem->txdelay[channel] = n;
+	    }
+	    else {
+	      p_modem->txdelay[channel] = DEFAULT_TXDELAY;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Invalid time for transmit delay. Using %d.\n", 
+			line, p_modem->txdelay[channel]);
+   	    }
+	  }
+
+/*
+ * TXTAIL 		- For transmit timing.
+ */
+
+	  else if (strcasecmp(t, "TXTAIL") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing time for TXTAIL command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 0 && n <= 255) {
+	      p_modem->txtail[channel] = n;
+	    }
+	    else {
+	      p_modem->txtail[channel] = DEFAULT_TXTAIL;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Invalid time for transmit timing. Using %d.\n", 
+			line, p_modem->txtail[channel]);
+   	    }
+	  }
+
+/*
+ * ==================== Digipeater parameters ==================== 
+ */
+
+	  else if (strcasecmp(t, "digipeat") == 0) {
+	    int from_chan, to_chan;
+	    int e;
+	    char message[100];
+	    	    
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Missing FROM-channel on line %d.\n", line);
+	      continue;
+	    }
+	    from_chan = atoi(t);
+	    if (from_chan < 0 || from_chan > p_digi_config->num_chans-1) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n", 
+							p_digi_config->num_chans-1, line);
+	      continue;
+	    }
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Missing TO-channel on line %d.\n", line);
+	      continue;
+	    }
+	    to_chan = atoi(t);
+	    if (to_chan < 0 || to_chan > p_digi_config->num_chans-1) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n", 
+							p_digi_config->num_chans-1, line);
+	      continue;
+	    }
+	
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Missing alias pattern on line %d.\n", line);
+	      continue;
+	    }
+	    e = regcomp (&(p_digi_config->alias[from_chan][to_chan]), t, REG_EXTENDED|REG_NOSUB);
+	    if (e != 0) {
+	      regerror (e, &(p_digi_config->alias[from_chan][to_chan]), message, sizeof(message));
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Invalid alias matching pattern on line %d:\n%s\n", 
+							line, message);
+	      continue;
+	    }
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Missing wide pattern on line %d.\n", line);
+	      continue;
+	    }
+	    e = regcomp (&(p_digi_config->wide[from_chan][to_chan]), t, REG_EXTENDED|REG_NOSUB);
+	    if (e != 0) {
+	      regerror (e, &(p_digi_config->wide[from_chan][to_chan]), message, sizeof(message));
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Invalid wide matching pattern on line %d:\n%s\n", 
+							line, message);
+	      continue;
+	    }
+
+	    p_digi_config->enabled[from_chan][to_chan] = 1;
+	    p_digi_config->preempt[from_chan][to_chan] = PREEMPT_OFF;
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t != NULL) {
+	      if (strcasecmp(t, "OFF") == 0) {
+	        p_digi_config->preempt[from_chan][to_chan] = PREEMPT_OFF;
+	      }
+	      else if (strcasecmp(t, "DROP") == 0) {
+	        p_digi_config->preempt[from_chan][to_chan] = PREEMPT_DROP;
+	      }
+	      else if (strcasecmp(t, "MARK") == 0) {
+	        p_digi_config->preempt[from_chan][to_chan] = PREEMPT_MARK;
+	      }
+	      else if (strcasecmp(t, "TRACE") == 0) {
+	        p_digi_config->preempt[from_chan][to_chan] = PREEMPT_TRACE;
+	      }
+	      else {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Config file: Expected OFF, DROP, MARK, or TRACE on line %d.\n", line);
+	      }
+
+	    }
+	  }
+
+/*
+ * DEDUPE 		- Time to suppress digipeating of duplicate packets.
+ */
+
+	  else if (strcasecmp(t, "DEDUPE") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing time for DEDUPE command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= 0 && n < 600) {
+	      p_digi_config->dedupe_time = n;
+	    }
+	    else {
+	      p_digi_config->dedupe_time = DEFAULT_DEDUPE;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Unreasonable value for dedupe time. Using %d.\n", 
+			line, p_digi_config->dedupe_time);
+   	    }
+	  }
+
+
+/*
+ * ==================== APRStt gateway ==================== 
+ */
+
+/*
+ * TTCORRAL 		- How to handle unknown positions
+ *
+ * TTCORRAL  latitude  longitude  offset-or-ambiguity 
+ */
+
+	  else if (strcasecmp(t, "TTCORRAL") == 0) {
+	    //int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing latitude for TTCORRAL command.\n", line);
+	      continue;
+	    }
+	    p_tt_config->corral_lat = parse_ll(t,LAT,line);
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing longitude for TTCORRAL command.\n", line);
+	      continue;
+	    }
+	    p_tt_config->corral_lon = parse_ll(t,LON,line);
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing longitude for TTCORRAL command.\n", line);
+	      continue;
+	    }
+	    p_tt_config->corral_offset = parse_ll(t,LAT,line);
+	    if (p_tt_config->corral_offset == 1 ||
+		p_tt_config->corral_offset == 2 ||
+	 	p_tt_config->corral_offset == 3) {
+	      p_tt_config->corral_ambiguity = p_tt_config->corral_offset;
+	      p_tt_config->corral_offset = 0;
+	    }
+
+	    //dw_printf ("DEBUG: corral %f %f %f %d\n", p_tt_config->corral_lat,
+	    //	p_tt_config->corral_lon, p_tt_config->corral_offset, p_tt_config->corral_ambiguity);
+	  }
+
+/*
+ * TTPOINT 		- Define a point represented by touch tone sequence.
+ *
+ * TTPOINT   pattern  latitude  longitude   
+ */
+	  else if (strcasecmp(t, "TTPOINT") == 0) {
+
+	    struct ttloc_s *tl;
+	    int j;
+
+	    assert (p_tt_config->ttloc_size >= 2);
+	    assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
+
+	    /* Allocate new space, but first, if already full, make larger. */
+	    if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
+	      p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
+	      p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
+	    }
+	    p_tt_config->ttloc_len++;
+	    assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
+
+	    tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
+	    tl->type = TTLOC_POINT;
+	    strcpy(tl->pattern, "");
+	    tl->point.lat = 0;
+	    tl->point.lon = 0;
+
+	    /* Pattern: B and digits */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing pattern for TTPOINT command.\n", line);
+	      continue;
+	    }
+	    strcpy (tl->pattern, t);
+
+	    if (t[0] != 'B') {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: TTPOINT pattern must begin with upper case 'B'.\n", line);
+	    }
+	    for (j=1; j<strlen(t); j++) {
+	      if ( ! isdigit(t[j])) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: TTPOINT pattern must be B and digits only.\n", line);
+	      }
+	    }
+
+	    /* Latitude */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing latitude for TTPOINT command.\n", line);
+	      continue;
+	    }
+	    tl->point.lat = parse_ll(t,LAT,line);
+
+	    /* Longitude */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing longitude for TTPOINT command.\n", line);
+	      continue;
+	    }
+	    tl->point.lon = parse_ll(t,LON,line);
+
+	    /* temp debugging */
+
+	    //for (j=0; j<p_tt_config->ttloc_len; j++) {
+	    //  dw_printf ("debug ttloc %d/%d %s\n", j, p_tt_config->ttloc_size, 
+	    //		p_tt_config->ttloc_ptr[j].pattern);
+	    //}
+	  }
+
+/*
+ * TTVECTOR 		- Touch tone location with bearing and distance.
+ *
+ * TTVECTOR   pattern  latitude  longitude  scale  unit  
+ */
+	  else if (strcasecmp(t, "TTVECTOR") == 0) {
+
+	    struct ttloc_s *tl;
+	    int j;
+	    double scale;
+	    double meters;
+
+	    assert (p_tt_config->ttloc_size >= 2);
+	    assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
+
+	    /* Allocate new space, but first, if already full, make larger. */
+	    if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
+	      p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
+	      p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
+	    }
+	    p_tt_config->ttloc_len++;
+	    assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
+
+	    tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
+	    tl->type = TTLOC_VECTOR;
+	    strcpy(tl->pattern, "");
+	    tl->vector.lat = 0;
+	    tl->vector.lon = 0;
+	    tl->vector.scale = 1;
+	   
+	    /* Pattern: B5bbbd... */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing pattern for TTVECTOR command.\n", line);
+	      continue;
+	    }
+	    strcpy (tl->pattern, t);
+
+	    if (t[0] != 'B') {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: TTVECTOR pattern must begin with upper case 'B'.\n", line);
+	    }
+	    if (strncmp(t+1, "5bbb", 4) != 0) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: TTVECTOR pattern would normally contain \"5bbb\".\n", line);
+	    }
+	    for (j=1; j<strlen(t); j++) {
+	      if ( ! isdigit(t[j]) && t[j] != 'b' && t[j] != 'd') {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: TTVECTOR pattern must contain only B, digits, b, and d.\n", line);
+	      }
+	    }
+
+	    /* Latitude */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing latitude for TTVECTOR command.\n", line);
+	      continue;
+	    }
+	    tl->vector.lat = parse_ll(t,LAT,line);
+
+	    /* Longitude */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing longitude for TTVECTOR command.\n", line);
+	      continue;
+	    }
+	    tl->vector.lon = parse_ll(t,LON,line);
+
+	    /* Longitude */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing scale for TTVECTOR command.\n", line);
+	      continue;
+	    }
+	    scale = atof(t);
+
+	    /* Unit. */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing unit for TTVECTOR command.\n", line);
+	      continue;
+	    }
+	    meters = 0;
+	    for (j=0; j<NUM_UNITS && meters == 0; j++) {
+	      if (strcasecmp(units[j].name, t) == 0) {
+	        meters = units[j].meters;
+	      }
+	    }
+	    if (meters == 0) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Unrecognized unit for TTVECTOR command.  Using miles.\n", line);
+	      meters = 1609.344;
+	    }
+	    tl->vector.scale = scale * meters;
+
+ 	    //dw_printf ("ttvector: %f meters\n", tl->vector.scale);
+
+	    /* temp debugging */
+
+	    //for (j=0; j<p_tt_config->ttloc_len; j++) {
+	    //  dw_printf ("debug ttloc %d/%d %s\n", j, p_tt_config->ttloc_size, 
+	    //		p_tt_config->ttloc_ptr[j].pattern);
+	    //}
+	  }
+
+/*
+ * TTGRID 		- Define a grid for touch tone locations.
+ *
+ * TTGRID   pattern  min-latitude  min-longitude  max-latitude  max-longitude
+ */
+	  else if (strcasecmp(t, "TTGRID") == 0) {
+
+	    struct ttloc_s *tl;
+	    int j;
+
+	    assert (p_tt_config->ttloc_size >= 2);
+	    assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
+
+	    /* Allocate new space, but first, if already full, make larger. */
+	    if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
+	      p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
+	      p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
+	    }
+	    p_tt_config->ttloc_len++;
+	    assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
+
+	    tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
+	    tl->type = TTLOC_GRID;
+	    strcpy(tl->pattern, "");
+	    tl->grid.lat0 = 0;
+	    tl->grid.lon0 = 0;
+	    tl->grid.lat9 = 0;
+	    tl->grid.lon9 = 0;
+
+	    /* Pattern: B [digit] x... y... */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing pattern for TTGRID command.\n", line);
+	      continue;
+	    }
+	    strcpy (tl->pattern, t);
+
+	    if (t[0] != 'B') {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: TTGRID pattern must begin with upper case 'B'.\n", line);
+	    }
+	    for (j=1; j<strlen(t); j++) {
+	      if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y') {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: TTGRID pattern must be B, optional digit, xxx, yyy.\n", line);
+	      }
+	    }
+
+	    /* Minimum Latitude - all zeros in received data */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing latitude for TTGRID command.\n", line);
+	      continue;
+	    }
+	    tl->grid.lat0 = parse_ll(t,LAT,line);
+
+	    /* Minimum Longitude - all zeros in received data */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing longitude for TTGRID command.\n", line);
+	      continue;
+	    }
+	    tl->grid.lon0 = parse_ll(t,LON,line);
+
+	    /* Maximum Latitude - all nines in received data */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing latitude for TTGRID command.\n", line);
+	      continue;
+	    }
+	    tl->grid.lat9 = parse_ll(t,LAT,line);
+
+	    /* Maximum Longitude - all nines in received data */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing longitude for TTGRID command.\n", line);
+	      continue;
+	    }
+	    tl->grid.lon0 = parse_ll(t,LON,line);
+
+	    /* temp debugging */
+
+	    //for (j=0; j<p_tt_config->ttloc_len; j++) {
+	    //  dw_printf ("debug ttloc %d/%d %s\n", j, p_tt_config->ttloc_size, 
+	    //	p_tt_config->ttloc_ptr[j].pattern);
+	    //}
+	  }
+
+/*
+ * TTUTM 		- Specify UTM zone for touch tone locations.
+ *
+ * TTUTM   pattern  zone [ scale [ x-offset y-offset ] ]
+ */
+	  else if (strcasecmp(t, "TTUTM") == 0) {
+
+	    struct ttloc_s *tl;
+	    int j;
+	    int znum;
+	    char *zlet;
+
+	    assert (p_tt_config->ttloc_size >= 2);
+	    assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
+
+	    /* Allocate new space, but first, if already full, make larger. */
+	    if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
+	      p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
+	      p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
+	    }
+	    p_tt_config->ttloc_len++;
+	    assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
+
+	    tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
+	    tl->type = TTLOC_UTM;
+	    strcpy(tl->pattern, "");
+	    strcpy(tl->utm.zone, "");
+	    tl->utm.scale = 1;
+	    tl->utm.x_offset = 0;
+	    tl->utm.y_offset = 0;
+
+	    /* Pattern: B [digit] x... y... */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing pattern for TTUTM command.\n", line);
+	      continue;
+	    }
+	    strcpy (tl->pattern, t);
+
+	    if (t[0] != 'B') {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: TTUTM pattern must begin with upper case 'B'.\n", line);
+	    }
+	    for (j=1; j<strlen(t); j++) {
+	      if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y') {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: TTUTM pattern must be B, optional digit, xxx, yyy.\n", line);
+	      }
+	    }
+
+	    /* Zone 1 - 60 and optional latitudinal letter. */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing zone for TTUTM command.\n", line);
+	      continue;
+	    }
+	    memset (tl->utm.zone, 0, sizeof (tl->utm.zone));
+	    strncpy (tl->utm.zone, t, sizeof (tl->utm.zone) - 1);
+
+            znum = strtoul(tl->utm.zone, &zlet, 10);
+
+            if (znum < 1 || znum > 60) {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Zone number is out of range.\n\n", line);
+              continue;
+            }
+
+            if (*zlet != '\0' && strchr ("CDEFGHJKLMNPQRSTUVWX", *zlet) == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Latitudinal band must be one of CDEFGHJKLMNPQRSTUVWX.\n\n", line);
+              continue;
+            }
+
+	    /* Optional scale. */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      continue;
+	    }
+	    tl->utm.scale = atof(t);
+
+	    /* Optional x offset. */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      continue;
+	    }
+	    tl->utm.x_offset = atof(t);
+
+	    /* Optional y offset. */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      continue;
+	    }
+	    tl->utm.y_offset = atof(t);
+	  }
+
+/*
+ * TTMACRO 		- Define compact message format with full expansion
+ *
+ * TTMACRO   pattern  definition
+ */
+	  else if (strcasecmp(t, "TTMACRO") == 0) {
+
+	    struct ttloc_s *tl;
+	    int j;
+	    //char ch;
+	    int p_count[3], d_count[3];
+
+	    assert (p_tt_config->ttloc_size >= 2);
+	    assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
+
+	    /* Allocate new space, but first, if already full, make larger. */
+	    if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
+	      p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
+	      p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
+	    }
+	    p_tt_config->ttloc_len++;
+	    assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
+
+	    tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
+	    tl->type = TTLOC_MACRO;
+	    strcpy(tl->pattern, "");
+
+	    /* Pattern: Any combination of digits, x, y, and z. */
+	    /* Also make note of which letters are used in pattern and defintition. */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing pattern for TTMACRO command.\n", line);
+	      continue;
+	    }
+	    strcpy (tl->pattern, t);
+
+	    p_count[0] = p_count[1] = p_count[2] = 0;
+
+	    for (j=0; j<strlen(t); j++) {
+	      if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y' && t[j] != 'z') {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: TTMACRO pattern can contain only digits and lower case x, y, or z.\n", line);
+	        continue;
+	      }
+	      if (t[j] >= 'x' && t[j] <= 'z') {
+		p_count[t[j]-'x']++;
+	      }
+	    }
+
+	    /* Now gather up the definition. */
+	    /* It can contain touch tone characters and lower case x, y, z for substitutions. */
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing definition for TTMACRO command.\n", line);
+	      tl->macro.definition = "";	/* Don't die on null pointer later. */
+	      continue;
+	    }
+	    tl->macro.definition = strdup(t);
+
+	    d_count[0] = d_count[1] = d_count[2] = 0;
+
+	    for (j=0; j<strlen(t); j++) {
+	      if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y' && t[j] != 'z' &&
+			t[j] != 'A' && t[j] != 'B' && t[j] != 'C' && t[j] != 'D' && 
+			t[j] != '*' && t[j] != '#') {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: TTMACRO definition can contain only 0-9, A, B, C, D, *, #, x, y, z.\n", line);
+	        continue;
+	      }
+	      if (t[j] >= 'x' && t[j] <= 'z') {
+		d_count[t[j]-'x']++;
+	      }
+	    }
+
+	    /* A little validity checking. */
+
+	    for (j=0; j<3; j++) {
+	      if (p_count[j] > 0 && d_count[j] == 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: '%c' is in TTMACRO pattern but is not used in definition.\n", line, 'x'+j);
+	      }
+	      if (d_count[j] > 0 && p_count[j] == 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Line %d: '%c' is referenced in TTMACRO definition but does not appear in the pattern.\n", line, 'x'+j);
+	      }
+	    }
+	  }
+
+/*
+ * TTOBJ 		- TT Object Report options.
+ *
+ * TTOBJ  xmit-chan  header 
+ */
+
+
+// TODO:  header can be generated automatically.  Should not be in config file.
+
+
+	  else if (strcasecmp(t, "TTOBJ") == 0) {
+	    int n;
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing transmit channel for TTOBJ command.\n", line);
+	      continue;
+	    }
+
+	    n = atoi(t);
+	    if (n < 0 || n > p_digi_config->num_chans-1) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Transmit channel must be in range of 0 to %d on line %d.\n", 
+							p_digi_config->num_chans-1, line);
+	      continue;
+	    }
+	    p_tt_config->obj_xmit_chan = n;
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing object header for TTOBJ command.\n", line);
+	      continue;
+	    }
+	    // TODO: Should do some validity checking.
+
+	    strncpy (p_tt_config->obj_xmit_header, t, sizeof(p_tt_config->obj_xmit_header));
+	
+	  }
+
+/*
+ * ==================== Internet gateway ==================== 
+ */
+
+/*
+ * IGSERVER 		- Name of IGate server.
+ *
+ * IGSERVER  hostname [ port ] 				-- original implementation.
+ *
+ * IGSERVER  hostname:port				-- more in line with usual conventions.
+ */
+
+	  else if (strcasecmp(t, "IGSERVER") == 0) {
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing IGate server name for IGSERVER command.\n", line);
+	      continue;
+	    }
+	    strncpy (p_igate_config->t2_server_name, t, sizeof(p_igate_config->t2_server_name)-1);
+
+	    /* If there is a : in the name, split it out as the port number. */
+
+	    t = strchr (p_igate_config->t2_server_name, ':');
+	    if (t != NULL) {
+	      *t = '\0';
+	      t++;
+	      int n = atoi(t);
+              if (n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) {
+	        p_igate_config->t2_server_port = n;
+	      }
+	      else {
+	        p_igate_config->t2_server_port = DEFAULT_IGATE_PORT;
+	        text_color_set(DW_COLOR_ERROR);
+                dw_printf ("Line %d: Invalid port number for IGate server. Using default %d.\n", 
+			line, p_igate_config->t2_server_port);
+   	      }
+	    }
+
+	    /* Alternatively, the port number could be separated by white space. */
+	    
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t != NULL) {
+	      int n = atoi(t);
+              if (n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) {
+	        p_igate_config->t2_server_port = n;
+	      }
+	      else {
+	        p_igate_config->t2_server_port = DEFAULT_IGATE_PORT;
+	        text_color_set(DW_COLOR_ERROR);
+                dw_printf ("Line %d: Invalid port number for IGate server. Using default %d.\n", 
+			line, p_igate_config->t2_server_port);
+   	      }
+	    }
+	    //printf ("DEBUG  server=%s   port=%d\n", p_igate_config->t2_server_name, p_igate_config->t2_server_port);
+	    //exit (0);
+	  }
+
+/*
+ * IGLOGIN 		- Login callsign and passcode for IGate server
+ *
+ * IGLOGIN  callsign  passcode
+ */
+
+	  else if (strcasecmp(t, "IGLOGIN") == 0) {
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing login callsign for IGLOGIN command.\n", line);
+	      continue;
+	    }
+	    // TODO: Wouldn't hurt to do validity checking of format.
+	    strncpy (p_igate_config->t2_login, t, sizeof(p_igate_config->t2_login)-1);
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing passcode for IGLOGIN command.\n", line);
+	      continue;
+	    }
+	    strncpy (p_igate_config->t2_passcode, t, sizeof(p_igate_config->t2_passcode)-1);
+	  }
+
+/*
+ * IGTXVIA 		- Transmit channel and VIA path for messages from IGate server
+ *
+ * IGTXVIA  channel  [ path ]
+ */
+
+	  else if (strcasecmp(t, "IGTXVIA") == 0) {
+	    int n;
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing transmit channel for IGTXVIA command.\n", line);
+	      continue;
+	    }
+
+	    n = atoi(t);
+	    if (n < 0 || n > p_digi_config->num_chans-1) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Transmit channel must be in range of 0 to %d on line %d.\n", 
+							p_digi_config->num_chans-1, line);
+	      continue;
+	    }
+	    p_igate_config->tx_chan = n;
+
+	    t = strtok (NULL, " \t\n\r");
+	    if (t != NULL) {
+	      char *p;
+	      p_igate_config->tx_via[0] = ',';
+	      strncpy (p_igate_config->tx_via + 1, t, sizeof(p_igate_config->tx_via)-2);
+	      for (p = p_igate_config->tx_via; *p != '\0'; p++) {
+	        if (islower(*p)) {
+		  *p = toupper(*p);	/* silently force upper case. */
+	        }
+	      }
+	    }
+	  }
+
+/*
+ * IGFILTER 		- Filter for messages from IGate server
+ *
+ * IGFILTER  filter-spec ... 
+ */
+
+	  else if (strcasecmp(t, "IGFILTER") == 0) {
+	    //int n;
+
+	    t = strtok (NULL, "\n\r");		/* Take rest of line as one string. */
+
+	    if (t != NULL && strlen(t) > 0) {
+	      p_igate_config->t2_filter = strdup (t);
+	    }
+	  }
+
+
+/*
+ * IGTXLIMIT 		- Limit transmissions during 1 and 5 minute intervals.
+ *
+ * IGTXLIMIT  one-minute-limit  five-minute-limit
+ */
+
+	  else if (strcasecmp(t, "IGTXLIMIT") == 0) {
+	    int n;
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing one minute limit for IGTXLIMIT command.\n", line);
+	      continue;
+	    }
+	    
+	    /* limits of 20 and 100 are unfriendly but not insane. */
+
+	    n = atoi(t);
+            if (n >= 1 && n <= 20) {
+	      p_igate_config->tx_limit_1 = n;
+	    }
+	    else {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Invalid one minute transmit limit. Using %d.\n", 
+				line, p_igate_config->tx_limit_1);
+   	    }
+
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing five minute limit for IGTXLIMIT command.\n", line);
+	      continue;
+	    }
+	    
+	    n = atoi(t);
+            if (n >= 1 && n <= 100) {
+	      p_igate_config->tx_limit_5 = n;
+	    }
+	    else {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Invalid one minute transmit limit. Using %d.\n", 
+				line, p_igate_config->tx_limit_5);
+   	    }
+	  }
+
+/*
+ * ==================== All the left overs ==================== 
+ */
+
+/*
+ * AGWPORT 		- Port number for "AGW TCPIP Socket Interface" 
+ */
+
+	  else if (strcasecmp(t, "AGWPORT") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing port number for AGWPORT command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) {
+	      p_misc_config->agwpe_port = n;
+	    }
+	    else {
+	      p_misc_config->agwpe_port = DEFAULT_AGWPE_PORT;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Invalid port number for AGW TCPIP Socket Interface. Using %d.\n", 
+			line, p_misc_config->agwpe_port);
+   	    }
+	  }
+
+/*
+ * KISSPORT 		- Port number for KISS over IP. 
+ */
+
+	  else if (strcasecmp(t, "KISSPORT") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing port number for KISSPORT command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) {
+	      p_misc_config->kiss_port = n;
+	    }
+	    else {
+	      p_misc_config->kiss_port = DEFAULT_KISS_PORT;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Invalid port number for KISS TCPIP Socket Interface. Using %d.\n", 
+			line, p_misc_config->kiss_port);
+   	    }
+	  }
+
+/*
+ * NULLMODEM		- Device name for our end of the virtual "null modem"
+ */
+	  else if (strcasecmp(t, "nullmodem") == 0) {
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Missing device name for my end of the 'null modem' on line %d.\n", line);
+	      continue;
+	    }
+	    else {
+	      strncpy (p_misc_config->nullmodem, t, sizeof(p_misc_config->nullmodem)-1);
+	    }
+	  }
+
+/*
+ * FIX_BITS 		- Attempt to fix frames with bad FCS. 
+ */
+
+	  else if (strcasecmp(t, "FIX_BITS") == 0) {
+	    int n;
+	    t = strtok (NULL, " ,\t\n\r");
+	    if (t == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Line %d: Missing value for FIX_BITS command.\n", line);
+	      continue;
+	    }
+	    n = atoi(t);
+            if (n >= RETRY_NONE && n <= RETRY_TWO_SEP) {
+	      p_modem->fix_bits = (retry_t)n;
+	    }
+	    else {
+	      p_modem->fix_bits = DEFAULT_FIX_BITS;
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Line %d: Invalid value for FIX_BITS. Using %d.\n", 
+			line, p_modem->fix_bits);
+   	    }
+	  }
+
+/*
+ * BEACON channel delay every message
+ *
+ * Original handcrafted style.  Removed in version 1.0.
+ */
+
+	  else if (strcasecmp(t, "BEACON") == 0) {
+	    	    
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Config file, line %d: Old style 'BEACON' has been replaced with new commands.\n", line);
+	    
+	  }
+
+
+/*
+ * PBEACON keyword=value ...
+ * OBEACON keyword=value ...
+ * TBEACON keyword=value ...
+ * CBEACON keyword=value ...
+ *
+ * New style with keywords for options.
+ */
+
+	  else if (strcasecmp(t, "PBEACON") == 0 ||
+		   strcasecmp(t, "OBEACON") == 0 ||
+		   strcasecmp(t, "TBEACON") == 0 ||
+		   strcasecmp(t, "CBEACON") == 0) {
+
+	    if (p_misc_config->num_beacons < MAX_BEACONS) {
+
+	      memset (&(p_misc_config->beacon[p_misc_config->num_beacons]), 0, sizeof(struct beacon_s));
+	      if (strcasecmp(t, "PBEACON") == 0) {
+	        p_misc_config->beacon[p_misc_config->num_beacons].btype = BEACON_POSITION;
+	      }
+	      else if (strcasecmp(t, "OBEACON") == 0) {
+	        p_misc_config->beacon[p_misc_config->num_beacons].btype = BEACON_OBJECT;
+	      }
+	      else if (strcasecmp(t, "TBEACON") == 0) {
+	        p_misc_config->beacon[p_misc_config->num_beacons].btype = BEACON_TRACKER;
+	      }
+	      else {
+	        p_misc_config->beacon[p_misc_config->num_beacons].btype = BEACON_CUSTOM;
+	      }
+
+	      /* Save line number because some errors will be reported later. */
+	      p_misc_config->beacon[p_misc_config->num_beacons].lineno = line;
+
+	      if (beacon_options(t + strlen("xBEACON") + 1, &(p_misc_config->beacon[p_misc_config->num_beacons]), line)) {
+	        p_misc_config->num_beacons++;
+	      }
+	    }
+	    else {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Maximum number of beacons exceeded on line %d.\n", line);
+	      continue;
+	    }
+	  }
+
+
+/*
+ * SMARTBEACONING fast_speed fast_rate slow_speed slow_rate turn_time turn_angle turn_slope
+ */
+
+	  else if (strcasecmp(t, "SMARTBEACON") == 0 ||	
+	           strcasecmp(t, "SMARTBEACONING") == 0) {
+
+	    int n;
+
+#define SB_NUM(name,sbvar,minn,maxx,unit)  								\
+	    t = strtok (NULL, " ,\t\n\r");							\
+	    if (t == NULL) {									\
+	      text_color_set(DW_COLOR_ERROR);							\
+	      dw_printf ("Line %d: Missing %s for SmartBeaconing.\n", line, name);		\
+	      continue;										\
+	    }											\
+	    n = atoi(t);									\
+            if (n >= minn && n <= maxx) {							\
+	      p_misc_config->sbvar = n;								\
+	    }											\
+	    else {										\
+	      text_color_set(DW_COLOR_ERROR);							\
+              dw_printf ("Line %d: Invalid %s for SmartBeaconing. Using default %d %s.\n",	\
+			line, name, p_misc_config->sbvar, unit);				\
+   	    }
+
+#define SB_TIME(name,sbvar,minn,maxx,unit)  							\
+	    t = strtok (NULL, " ,\t\n\r");							\
+	    if (t == NULL) {									\
+	      text_color_set(DW_COLOR_ERROR);							\
+	      dw_printf ("Line %d: Missing %s for SmartBeaconing.\n", line, name);		\
+	      continue;										\
+	    }											\
+	    n = parse_interval(t,line);								\
+            if (n >= minn && n <= maxx) {							\
+	      p_misc_config->sbvar = n;								\
+	    }											\
+	    else {										\
+	      text_color_set(DW_COLOR_ERROR);							\
+              dw_printf ("Line %d: Invalid %s for SmartBeaconing. Using default %d %s.\n",	\
+			line, name, p_misc_config->sbvar, unit);				\
+   	    }
+
+
+	    SB_NUM  ("fast speed", sb_fast_speed,  2,   90,  "MPH")
+	    SB_TIME ("fast rate",  sb_fast_rate,  10,  300,  "seconds")
+
+	    SB_NUM  ("slow speed", sb_slow_speed,  1,   30,  "MPH")
+	    SB_TIME ("slow rate",  sb_slow_rate,  30, 3600,  "seconds")
+
+	    SB_TIME ("turn time",  sb_turn_time,   5,  180,  "seconds")
+	    SB_NUM  ("turn angle", sb_turn_angle,  5,   90,  "degrees")
+	    SB_NUM  ("turn slope", sb_turn_slope,  1,  255,  "deg*mph")
+
+	    /* If I was ambitious, I might allow optional */
+	    /* unit at end for miles or km / hour. */
+
+	    p_misc_config->sb_configured = 1;
+	  }
+
+/*
+ * Invalid command.
+ */
+	  else {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Config file: Unrecognized command '%s' on line %d.\n", t, line);
+	  }  
+
+	}
+
+	fclose (fp);
+
+/*
+ * A little error checking for option interactions.
+ */
+
+/*
+ * Require that MYCALL be set when digipeating or IGating.
+ *
+ * Suggest that beaconing be enabled when digipeating.
+ */
+	int i, j, k, b;
+
+	for (i=0; i<p_digi_config->num_chans; i++) {
+	  for (j=0; j<p_digi_config->num_chans; j++) {
+
+	    if (p_digi_config->enabled[i][j]) {
+
+	      if (strcmp(p_digi_config->mycall[i], "NOCALL") == 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Config file: MYCALL must be set for receive channel %d before digipeating is allowed.\n", i);
+	        p_digi_config->enabled[i][j] = 0;
+	      }
+
+	      if (strcmp(p_digi_config->mycall[j], "NOCALL") == 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Config file: MYCALL must be set for transmit channel %d before digipeating is allowed.\n", i); 
+	        p_digi_config->enabled[i][j] = 0;
+	      }
+
+	      b = 0;
+	      for (k=0; k<p_misc_config->num_beacons; k++) {
+	        if (p_misc_config->beacon[p_misc_config->num_beacons].chan == j) b++;
+	      }
+	      if (b == 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Config file: Beaconing should be configured for channel %d when digipeating is enabled.\n", i); 
+	        p_digi_config->enabled[i][j] = 0;
+	      }
+	    }
+	  }
+
+	  if (strlen(p_igate_config->t2_login) > 0) {
+
+	    if (strcmp(p_digi_config->mycall[i], "NOCALL") == 0) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: MYCALL must be set for receive channel %d before Rx IGate is allowed.\n", i);
+	      strcpy (p_igate_config->t2_login, "");
+	    }
+	    if (p_igate_config->tx_chan >= 0 && 
+			strcmp(p_digi_config->mycall[p_igate_config->tx_chan], "NOCALL") == 0) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: MYCALL must be set for transmit channel %d before Tx IGate is allowed.\n", i);
+	      p_igate_config->tx_chan = -1;
+	    }
+	  }
+
+	}
+
+} /* end config_init */
+
+
+/*
+ * Parse the PBEACON or OBEACON options.
+ * Returns 1 for success, 0 for serious error.
+ */
+
+static int beacon_options(char *cmd, struct beacon_s *b, int line)
+{
+	char options[1000];
+	char *o;
+	char *t;
+	char *p;
+	int q;
+	char temp_symbol[100];
+	int ok;
+	
+	strcpy (temp_symbol, "");
+
+	b->chan = 0;
+	b->delay = 60;
+	b->every = 600;
+	//b->delay = 6;		// TODO: temp. remove
+	//b->every = 3600;
+	b->lat = G_UNKNOWN;
+	b->lon = G_UNKNOWN;
+	b->symtab = '/';
+	b->symbol = '-';	/* house */
+
+/*
+ * cmd should be rest of command line after ?BEACON was removed.
+ *
+ * Quoting is required for any values containing spaces.
+ * This could happen for an object name, comment, symbol description, ...
+ * To prevent strtok from stopping at those spaces, change them to 
+ * non-breaking space character temporarily.  After spliting everything
+ * up at white space, change them back to normal spaces.
+ */
+ 
+#define NBSP (' ' + 0x80)
+
+	p = cmd;		/* Process from here. */
+	o = options;		/* to here. */
+	q = 0;			/* Keep track of whether in quoted part. */
+
+	for ( ; *p != '\0' ; p++) {
+
+	  switch (*p) {
+
+	    case '"':
+	      if (!q) {		/* opening quote */
+	        if (*(p-1) != '=') {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("Config file: line %d: Suspicious use of \" not after =.\n", line);
+	 	  dw_printf ("Suggestion: Double it and quote entire value.\n");
+	          *o++ = '"';	/* Treat as regular character. */
+	        }
+	        else {
+	          q = 1;
+	        }
+	      }
+	      else {		/* embedded or closing quote */
+	        if (*(p+1) == '"') {
+	          *o++ = '"';	/* reduce double to single */
+	          p++;
+	        }
+                else if (isspace(*(p+1)) || *(p+1) == '\0') {
+	          q = 0;
+	        }
+	        else {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("Config file: line %d: Suspicious use of \" not at end of value.\n", line);
+	 	  dw_printf ("Suggestion: Double it and quote entire value.\n");
+	          *o++ = '"';	/* Treat as regular character. */
+	        }		  
+	      }
+	      break;
+
+	    case ' ':
+
+	      *o++ = q ? NBSP : ' ';
+	      break;
+
+	    default:
+	      *o++ = *p;
+	      break;
+	  }
+	}
+	*o = '\0';
+
+	for (t = strtok (options, " \t\n\r"); t != NULL; t = strtok (NULL, " \t\n\r")) {
+
+	  char keyword[20];
+	  char value[200];
+	  char *e;
+	  char *p;
+	  //int q;
+
+
+	  e = strchr(t, '=');
+	  if (e == NULL) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Config file: No = found in, %s, on line %d.\n", t, line);
+	    return (0);
+	  }
+	  *e = '\0';
+	  strcpy (keyword, t);
+	  strcpy (value, e+1);
+
+/* Put back normal spaces. */
+
+	  for (p = value; *p != '\0'; p++) {
+	    // char is signed for MinGW!
+	    if (((int)(*p) & 0xff) == NBSP) *p = ' ';
+	  }
+
+	  if (strcasecmp(keyword, "DELAY") == 0) {
+	    b->delay = parse_interval(value,line);
+	  }
+	  else if (strcasecmp(keyword, "EVERY") == 0) {
+	    b->every = parse_interval(value,line);
+	  }
+	  else if (strcasecmp(keyword, "SENDTO") == 0) {
+	    if (value[0] == 'i' || value[0] == 'I') {
+	       b->chan = -1;
+	    }
+	    else {
+	       b->chan = atoi(value);
+	    }
+	  }
+	  else if (strcasecmp(keyword, "VIA") == 0) {
+	    b->via = strdup(value);
+	    for (p = b->via; *p != '\0'; p++) {
+	      if (islower(*p)) {
+	        *p = toupper(*p);	/* silently force upper case. */
+	      }
+	    }
+	  }
+	  else if (strcasecmp(keyword, "INFO") == 0) {
+	    b->custom_info = strdup(value);
+	  }
+	  else if (strcasecmp(keyword, "OBJNAME") == 0) {
+	    strncpy(b->objname, value, 9);
+	  }
+	  else if (strcasecmp(keyword, "LAT") == 0) {
+	    b->lat = parse_ll (value, LAT, line);
+	  }
+	  else if (strcasecmp(keyword, "LONG") == 0 || strcasecmp(keyword, "LON") == 0) {
+	    b->lon = parse_ll (value, LON, line);
+	  }
+	  else if (strcasecmp(keyword, "SYMBOL") == 0) {
+	    /* Defer processing in case overlay appears later. */
+	    strcpy (temp_symbol, value);
+	  }
+	  else if (strcasecmp(keyword, "OVERLAY") == 0) {
+	    if (strlen(value) == 1 && (isupper(value[0]) || isdigit(value[0]))) {
+	      b->symtab = value[0];
+	    }
+	    else {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file: Overlay must be one character in range of 0-9 or A-Z, upper case only, on line %d.\n", line);
+	    }
+	  }
+	  else if (strcasecmp(keyword, "POWER") == 0) {
+	    b->power = atoi(value);
+	  }
+	  else if (strcasecmp(keyword, "HEIGHT") == 0) {
+	    b->height = atoi(value);
+	  }
+	  else if (strcasecmp(keyword, "GAIN") == 0) {
+	    b->gain = atoi(value);
+	  }
+	  else if (strcasecmp(keyword, "DIR") == 0 || strcasecmp(keyword, "DIRECTION") == 0) {
+	    strncpy(b->dir, value, 2);
+	  }
+	  else if (strcasecmp(keyword, "FREQ") == 0) {
+	    b->freq = atof(value);
+	  }
+	  else if (strcasecmp(keyword, "TONE") == 0) {
+	    b->tone = atof(value);
+	  }
+	  else if (strcasecmp(keyword, "OFFSET") == 0 || strcasecmp(keyword, "OFF") == 0) {
+	    b->offset = atof(value);
+	  }
+	  else if (strcasecmp(keyword, "COMMENT") == 0) {
+	    b->comment = strdup(value);
+	  }
+	  else if (strcasecmp(keyword, "COMPRESS") == 0 || strcasecmp(keyword, "COMPRESSED") == 0) {
+	    b->compress = atoi(value);
+	  }
+	  else {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Config file, line %d: Invalid option keyword, %s.\n", line, keyword);
+	    return (0);
+	  }
+	}
+
+/*
+ * Process symbol now that we have any later overlay.
+ */
+	if (strlen(temp_symbol) > 0) {
+
+	  if (strlen(temp_symbol) == 2 && 
+		(temp_symbol[0] == '/' || temp_symbol[0] == '\\' || isupper(temp_symbol[0]) || isdigit(temp_symbol[0])) &&
+		temp_symbol[1] >= '!' && temp_symbol[1] <= '~') {
+
+	    /* Explicit table and symbol. */
+
+	    if (isupper(b->symtab) || isdigit(b->symtab)) {
+	      b->symbol = temp_symbol[1];
+	    } 
+	    else {
+	      b->symtab = temp_symbol[0];
+	      b->symbol = temp_symbol[1];
+	    }
+	  }
+	  else {
+
+	    /* Try to look up by description. */
+	    ok = symbols_code_from_description (b->symtab, temp_symbol, &(b->symtab), &(b->symbol));
+	    if (!ok) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file, line %d: Could not find symbol matching %s.\n", line, temp_symbol);
+	    }
+	  }
+	}
+
+	return (1);
+}
+
+/* end config.c */
diff --git a/config.h b/config.h
new file mode 100755
index 0000000..8ac91b6
--- /dev/null
+++ b/config.h
@@ -0,0 +1,130 @@
+
+/*----------------------------------------------------------------------------
+ * 
+ * Name:	config.h
+ *
+ * Purpose:	
+ *
+ * Description:	
+ *
+ *-----------------------------------------------------------------------------*/
+
+
+#ifndef CONFIG_H
+#define CONFIG_H 1
+
+#include "audio.h"		/* for struct audio_s */
+#include "digipeater.h"		/* for struct digi_config_s */
+#include "aprs_tt.h"		/* for struct tt_config_s */
+#include "igate.h"		/* for struct igate_config_s */
+
+/*
+ * All the leftovers.
+ * This wasn't thought out.  It just happened.
+ */
+
+enum beacon_type_e { BEACON_IGNORE, BEACON_POSITION, BEACON_OBJECT, BEACON_TRACKER, BEACON_CUSTOM };
+
+#define MAX_BEACONS 30
+
+struct misc_config_s {
+
+	int num_channels;	/* Number of radio channels. */
+
+	int agwpe_port;		/* Port number for the �AGW TCPIP Socket Interface� */
+	int kiss_port;		/* Port number for the �KISS� protocol. */
+	int enable_kiss_pt;	/* Enable pseudo terminal for KISS. */
+				/* Want this to be off by default because it hangs */
+				/* after a while if nothing is reading from other end. */
+
+	char nullmodem[40];	/* Serial port name for our end of the */
+				/* virtual null modem for native Windows apps. */
+
+	int sb_configured;	/* TRUE if SmartBeaconing is configured. */
+	int sb_fast_speed;	/* MPH */
+	int sb_fast_rate;	/* seconds */
+	int sb_slow_speed;	/* MPH */
+	int sb_slow_rate;	/* seconds */
+	int sb_turn_time;	/* seconds */
+	int sb_turn_angle;	/* degrees */
+	int sb_turn_slope;	/* degrees * MPH */
+
+ 			
+	int num_beacons;	/* Number of beacons defined. */
+
+	struct beacon_s {
+
+	  enum beacon_type_e btype;	/* Position or object. */
+
+	  int lineno;		/* Line number from config file for later error messages. */
+
+	  int chan;		/* Send to Channel for transmission. -1 for IGate.  */
+
+	  int delay;		/* Seconds to delay before first transmission. */
+
+	  int every;		/* Time between transmissions, seconds. */
+				/* Remains fixed for PBEACON and OBEACON. */
+				/* Dynamically adjusted for TBEACON. */
+
+	  time_t next;		/* Unix time to transmit next one. */
+
+	  int compress;		/* Use more compact form? */
+
+	  char objname[10];	/* Object name.  Any printable characters. */
+
+	  char *via;		/* Path, e.g. "WIDE1-1,WIDE2-1" or NULL. */
+
+	  char *custom_info;	/* Info part for handcrafted custom beacon. */
+				/* Ignore the rest below if this is set. */
+
+	  double lat;		/* Latitude and longitude. */
+	  double lon;
+
+	  char symtab;		/* Symbol table: / or \ or overlay character. */
+	  char symbol;		/* Symbol code. */
+
+	  float power;		/* For PHG. */
+	  float height;
+	  float gain;		/* Original protocol spec was unclear. */
+				/* Addendum 1.1 clarifies it is dBi not dBd. */
+
+	  char dir[3];		/* 1 or 2 of N,E,W,S, or empty for omni. */
+
+	  float freq;		/* MHz. */
+	  float tone;		/* Hz. */
+	  float offset;		/* MHz. */
+	
+	  char *comment;	/* Comment or NULL. */
+
+
+	} beacon[MAX_BEACONS];
+
+};
+
+
+#define MIN_IP_PORT_NUMBER 1024
+#define MAX_IP_PORT_NUMBER 49151
+
+
+#define DEFAULT_AGWPE_PORT 8000		/* Like everyone else. */
+#define DEFAULT_KISS_PORT 8001		/* Above plus 1. */
+
+
+#define DEFAULT_NULLMODEM "COM3"  	/* should be equiv. to /dev/ttyS2 on Cygwin */
+
+
+
+
+extern void config_init (char *fname, struct audio_s *p_modem, 
+			struct digi_config_s *digi_config,
+			struct tt_config_s *p_tt_config,
+			struct igate_config_s *p_igate_config,
+			struct misc_config_s *misc_config);
+
+
+
+#endif /* CONFIG_H */
+
+/* end config.h */
+
+
diff --git a/decode_aprs.c b/decode_aprs.c
new file mode 100755
index 0000000..089ba8b
--- /dev/null
+++ b/decode_aprs.c
@@ -0,0 +1,3948 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012,2013,2014  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * File:	decode_aprs.c
+ *
+ * Purpose:	Decode the information part of APRS frame.
+ *
+ * Description: Present the packet contents in human readable format.
+ *		This is a fairly complete implementation with error messages
+ *		pointing out various specication violations. 
+ *
+ *
+ *
+ * Assumptions:	ax25_from_frame() has been called to 
+ *		separate the header and information.
+ *
+ *
+ *------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <time.h>
+#include <assert.h>
+#include <stdlib.h>	/* for atof */
+#include <string.h>	/* for strtok */
+#if __WIN32__
+char *strsep(char **stringp, const char *delim);
+#endif
+#include <math.h>	/* for pow */
+#include <ctype.h>	/* for isdigit */
+#include <fcntl.h>
+
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 1
+#endif
+#include "regex.h"
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "symbols.h"
+#include "latlong.h"
+
+#define TRUE 1
+#define FALSE 0
+
+
+#define METERS_TO_FEET(x) ((x) * 3.2808399)
+#define KNOTS_TO_MPH(x) ((x) * 1.15077945)
+#define KM_TO_MILES(x) ((x) * 0.621371192)
+#define MBAR_TO_INHG(x) ((x) * 0.0295333727)
+
+
+/* Position & symbol fields common to several message formats. */
+
+typedef struct {
+	  char lat[8];
+	  char sym_table_id;		/* / \ 0-9 A-Z */
+	  char lon[9];
+	  char symbol_code;
+	} position_t;
+
+typedef struct {
+	  char sym_table_id;		/* / \ a-j A-Z */
+					/* "The presence of the leading Symbol Table Identifier */
+					/* instead of a digit indicates that this is a compressed */
+					/* Position Report and not a normal lat/long report." */
+					/* "a-j" is not a typographical error. */
+					/* The first 10 lower case letters represent the overlay */
+					/* characters of 0-9 in the compressed format. */
+
+	  char y[4];			/* Compressed Latitude. */
+	  char x[4];			/* Compressed Longitude. */
+	  char symbol_code;
+	  char c;			/* Course/speed or altitude. */
+	  char s;
+	  char t	;		/* Compression type. */
+	} compressed_position_t;
+
+
+static void print_decoded (void);
+
+static void aprs_ll_pos (unsigned char *, int);
+static void aprs_ll_pos_time (unsigned char *, int);
+static void aprs_raw_nmea (unsigned char *, int);
+static void aprs_mic_e (packet_t, unsigned char *, int);
+//static void aprs_compressed_pos (unsigned char *, int);
+static void aprs_message (unsigned char *, int);
+static void aprs_object (unsigned char *, int);
+static void aprs_item (unsigned char *, int);
+static void aprs_station_capabilities (char *, int);
+static void aprs_status_report (char *, int);
+static void aprs_telemetry (char *, int);
+static void aprs_raw_touch_tone (char *, int);
+static void aprs_morse_code (char *, int);
+static void aprs_positionless_weather_report (unsigned char *, int);
+static void weather_data (char *wdata, int wind_prefix);
+static void aprs_ultimeter (char *, int);
+static void third_party_header (char *, int);
+
+
+static void decode_position (position_t *ppos);
+static void decode_compressed_position (compressed_position_t *ppos);
+
+static double get_latitude_8 (char *p);
+static double get_longitude_9 (char *p);
+
+static double get_latitude_nmea (char *pstr, char *phemi);
+static double get_longitude_nmea (char *pstr, char *phemi);
+
+static time_t get_timestamp (char *p);
+static int get_maidenhead (char *p);
+
+static int data_extension_comment (char *pdext);
+static void decode_tocall (char *dest);
+//static void get_symbol (char dti, char *src, char *dest);
+static void process_comment (char *pstart, int clen);
+
+
+/*
+ * Information extracted from the message.
+ */
+
+/* for unknown values. */
+
+//#define G_UNKNOWN -999999
+
+
+static char g_msg_type[30];		/* Message type. */
+
+static char g_symbol_table;		/* The Symbol Table Identifier character selects one */
+					/* of the two Symbol Tables, or it may be used as */
+					/* single-character (alpha or numeric) overlay, as follows: */
+					
+					/*	/ 	Primary Symbol Table (mostly stations) */
+
+					/* 	\ 	Alternate Symbol Table (mostly Objects) */
+
+					/*	0-9 	Numeric overlay. Symbol from Alternate Symbol */
+					/*		Table (uncompressed lat/long data format) */
+
+					/*	a-j	Numeric overlay. Symbol from Alternate */
+					/*		Symbol Table (compressed lat/long data */
+					/*		format only). i.e. a-j maps to 0-9 */
+
+					/*	A-Z	Alpha overlay. Symbol from Alternate Symbol Table */
+
+
+static char g_symbol_code;		/* Where the Symbol Table Identifier is 0-9 or A-Z (or a-j */
+					/* with compressed position data only), the symbol comes from */
+					/* the Alternate Symbol Table, and is overlaid with the */
+					/* identifier (as a single digit or a capital letter). */
+
+static double g_lat, g_lon;		/* Location, degrees.  Negative for South or West. */
+					/* Set to G_UNKNOWN if missing or error. */
+
+static char g_maidenhead[9];		/* 4 or 6 (or 8?) character maidenhead locator. */
+
+static char g_name[20];			/* Object or item name. */
+
+static float g_speed;			/* Speed in MPH.  */
+
+static float g_course;			/* 0 = North, 90 = East, etc. */
+	
+static int g_power;			/* Transmitter power in watts. */
+
+static int g_height;			/* Antenna height above average terrain, feet. */
+
+static int g_gain;			/* Antenna gain in dB. */
+
+static char g_directivity[10];		/* Direction of max signal strength */
+
+static float g_range;			/* Precomputed radio range in miles. */
+
+static float g_altitude;		/* Feet above median sea level.  */
+
+static char g_mfr[80];			/* Manufacturer or application. */
+
+static char g_mic_e_status[30];		/* MIC-E message. */
+
+static char g_freq[40];			/* Frequency, tone, xmit offset */
+
+static char g_comment[256];		/* Comment. */
+
+/*------------------------------------------------------------------
+ *
+ * Function:	decode_aprs
+ *
+ * Purpose:	Optionally print packet then decode it.
+ *
+ * Inputs:	src	- Source Station.
+ *
+ *			  The SSID is used as a last resort for the
+ *			  displayed symbol if not specified in any other way.
+ *
+ *		dest	- Destination Station.
+ *
+ *			  Certain destinations (GPSxxx, SPCxxx, SYMxxx) can
+ *			  be used to specify the display symbol.
+ *			  For the MIC-E format (used by Kenwood D7, D700), the
+ *			  "destination" is really the latitude.
+ *
+ *		pinfo 	- pointer to information field.
+ *		info_len - length of the information field.
+ *
+ * Outputs:	Variables above:
+ *
+ *			g_symbol_table, g_symbol_code,
+ *			g_lat, g_lon, 
+ *			g_speed, g_course, g_altitude,
+ *			g_comment
+ *			... and others...
+ *
+ *		Other functions are then called to retrieve the information.
+ *
+ * Bug:		This is not thread-safe because it uses static data and strtok.
+ *
+ *------------------------------------------------------------------*/
+
+void decode_aprs (packet_t pp)
+{
+	//int naddr;
+	//int err;
+	char src[AX25_MAX_ADDR_LEN], dest[AX25_MAX_ADDR_LEN];
+	//char *p;
+	//int ssid;
+	unsigned char *pinfo;
+	int info_len;
+
+
+  	info_len = ax25_get_info (pp, &pinfo);
+
+	sprintf (g_msg_type, "Unknown message type %c", *pinfo);
+
+	g_symbol_table = '/';
+	g_symbol_code = ' ';		/* What should we have for default? */
+
+	g_lat = G_UNKNOWN;
+	g_lon = G_UNKNOWN;
+	strcpy (g_maidenhead, "");
+
+	strcpy (g_name, "");
+	g_speed = G_UNKNOWN;
+	g_course = G_UNKNOWN;
+
+	g_power = G_UNKNOWN;
+	g_height = G_UNKNOWN;
+	g_gain = G_UNKNOWN;
+	strcpy (g_directivity, "");
+
+	g_range = G_UNKNOWN;
+	g_altitude = G_UNKNOWN;
+	strcpy(g_mfr, "");
+	strcpy(g_mic_e_status, "");
+	strcpy(g_freq, "");
+	strcpy (g_comment, "");
+
+/*
+ * Extract source and destination including the SSID.
+ */
+	
+	ax25_get_addr_with_ssid (pp, AX25_SOURCE, src);
+	ax25_get_addr_with_ssid (pp, AX25_DESTINATION, dest);
+
+
+	switch (*pinfo) {	/* "DTI" data type identifier. */
+
+	    case '!':		/* Position without timestamp (no APRS messaging). */
+				/* or Ultimeter 2000 WX Station */
+
+	    case '=':		/* Position without timestamp (with APRS messaging). */
+
+	      if (strncmp((char*)pinfo, "!!", 2) == 0)
+	      {
+		aprs_ultimeter ((char*)pinfo, info_len);
+	      }
+	      else
+	      {	     
+	        aprs_ll_pos (pinfo, info_len);
+	      }
+	      break;
+
+
+	    //case '#':		/* Peet Bros U-II Weather station */
+	    //case '*':		/* Peet Bros U-II Weather station */
+	      //break;
+		
+	    case '$':		/* Raw GPS data or Ultimeter 2000 */
+		
+	      if (strncmp((char*)pinfo, "$ULTW", 5) == 0)
+	      {
+		aprs_ultimeter ((char*)pinfo, info_len);
+	      }
+	      else
+	      {
+	        aprs_raw_nmea (pinfo, info_len);
+	      }
+	      break;
+
+	    case '\'':		/* Old Mic-E Data (but Current data for TM-D700) */
+	    case '`':		/* Current Mic-E Data (not used in TM-D700) */
+
+	      aprs_mic_e (pp, pinfo, info_len);
+	      break;
+
+	    case ')':		/* Item. */
+
+	      aprs_item (pinfo, info_len);
+	      break;
+		
+	    case '/':		/* Position with timestamp (no APRS messaging) */
+	    case '@':		/* Position with timestamp (with APRS messaging) */
+
+	      aprs_ll_pos_time (pinfo, info_len);
+	      break;
+
+
+	    case ':':		/* Message */
+
+	      aprs_message (pinfo, info_len);
+	      break;
+
+	    case ';':		/* Object */
+
+	      aprs_object (pinfo, info_len);
+	      break;
+
+	    case '<':		/* Station Capabilities */
+
+	      aprs_station_capabilities ((char*)pinfo, info_len);
+	      break;
+
+	    case '>':		/* Status Report */
+
+	      aprs_status_report ((char*)pinfo, info_len);
+	      break;
+
+	    //case '?':		/* Query */
+	      //break;
+		
+	    case 'T':		/* Telemetry */
+	      aprs_telemetry ((char*)pinfo, info_len);
+	      break;
+
+	    case '_':		/* Positionless Weather Report */
+
+	      aprs_positionless_weather_report (pinfo, info_len);
+	      break;
+
+	    case '{':		/* user defined data */
+				/* http://www.aprs.org/aprs11/expfmts.txt */
+
+	      if (strncmp((char*)pinfo, "{tt", 3) == 0) {
+	        aprs_raw_touch_tone (pinfo, info_len);
+	      }
+	      else if (strncmp((char*)pinfo, "{mc", 3) == 0) {
+	        aprs_morse_code ((char*)pinfo, info_len);
+	      }
+	      else {
+	        //aprs_user_defined (pinfo, info_len);
+	      }
+	      break;
+
+	    case 't':		/* Raw touch tone data - NOT PART OF STANDARD */
+				/* Used to convey raw touch tone sequences to */
+				/* to an application that might want to interpret them. */
+				/* Might move into user defined data, above. */
+
+	      aprs_raw_touch_tone ((char*)pinfo, info_len);
+	      break;
+
+	    case 'm':		/* Morse Code data - NOT PART OF STANDARD */
+				/* Used by APRStt gateway to put audible responses */
+				/* into the transmit queue.  Could potentially find */
+				/* other uses such as CW ID for station. */
+				/* Might move into user defined data, above. */
+
+	      aprs_morse_code ((char*)pinfo, info_len);
+	      break;
+
+	    case '}':		/* third party header */
+
+	      third_party_header ((char*)pinfo, info_len);
+	      break;
+
+
+	    //case '\r':		/* CR or LF? */
+	    //case '\n':
+	
+	      //break;
+
+	    default:
+
+	      break;
+	}
+
+
+/*
+ * Look in other locations if not found in information field.
+ */
+
+	if (g_symbol_table == ' ' || g_symbol_code == ' ') {
+
+	  symbols_from_dest_or_src (*pinfo, src, dest, &g_symbol_table, &g_symbol_code);
+	}
+
+/*
+ * Application might be in the destination field for most message types.
+ * MIC-E format has part of location in the destination field.
+ */
+
+	switch (*pinfo) {	/* "DTI" data type identifier. */
+
+	  case '\'':		/* Old Mic-E Data */
+	  case '`':		/* Current Mic-E Data */
+	    break;
+
+	  default:
+	    decode_tocall (dest);
+	    break;
+	}
+	
+/*
+ * Print it all out in human readable format.
+ */
+	print_decoded ();
+}
+
+
+static void print_decoded (void) {
+
+	char stemp[200];
+	char tmp2[2];
+	double absll;
+	char news;
+	int deg;
+	double min;
+	char s_lat[30];
+	char s_lon[30];
+	int n;
+	char symbol_description[100];
+
+/*
+ * First line has:
+ * - message type 
+ * - object name
+ * - symbol
+ * - manufacturer/application
+ * - mic-e status
+ * - power/height/gain, range
+ */
+	strcpy (stemp, g_msg_type);
+
+	if (strlen(g_name) > 0) {
+	  strcat (stemp, ", \"");
+	  strcat (stemp, g_name);
+	  strcat (stemp, "\"");
+	}
+
+	symbols_get_description (g_symbol_table, g_symbol_code, symbol_description);	
+	strcat (stemp, ", ");
+	strcat (stemp, symbol_description);
+
+	if (strlen(g_mfr) > 0) {
+	  strcat (stemp, ", ");
+	  strcat (stemp, g_mfr);
+	}
+
+	if (strlen(g_mic_e_status) > 0) {
+	  strcat (stemp, ", ");
+	  strcat (stemp, g_mic_e_status);
+	}
+
+
+	if (g_power > 0) {
+	  char phg[100];
+
+	  sprintf (phg, ", %d W height=%d %ddBi %s", g_power, g_height, g_gain, g_directivity);
+	  strcat (stemp, phg);
+	}
+
+	if (g_range > 0) {
+	  char rng[100];
+
+	  sprintf (rng, ", range=%.1f", g_range);
+	  strcat (stemp, rng);
+	}
+	text_color_set(DW_COLOR_DECODED);
+	dw_printf("%s\n", stemp);
+
+/*
+ * Second line has:
+ * - Latitude
+ * - Longitude
+ * - speed
+ * - direction
+ * - altitude
+ * - frequency
+ */
+
+
+/*
+ * Convert Maidenhead locator to latitude and longitude.
+ * 
+ * Any example was checked for each hemihemisphere using
+ * http://www.amsat.org/cgi-bin/gridconv
+ *
+ * Bug: This does not check for invalid values.
+ */
+
+	if (strlen(g_maidenhead) > 0) {
+	  dw_printf("Grid square = %s, ", g_maidenhead);
+
+	  if (g_lat == G_UNKNOWN && g_lon == G_UNKNOWN) {
+	
+	    g_lon = (toupper(g_maidenhead[0]) - 'A') * 20 - 180;
+	    g_lat = (toupper(g_maidenhead[1]) - 'A') * 10 - 90;
+
+	    g_lon += (g_maidenhead[2] - '0') * 2;
+	    g_lat += (g_maidenhead[3] - '0');
+
+	    if (strlen(g_maidenhead) >=6) {
+	      g_lon += (toupper(g_maidenhead[4]) - 'A') * 5.0 / 60.0;
+	      g_lat += (toupper(g_maidenhead[5]) - 'A') * 2.5 / 60.0;
+
+	      g_lon += 2.5 / 60.0;	/* Move from corner to center of square */
+	      g_lat += 1.25 / 60.0;
+	    }
+	    else {
+	      g_lon += 1.0;	/* Move from corner to center of square */
+	      g_lat += 0.5;
+	    }
+	  }
+	}
+
+	strcpy (stemp, "");
+
+	if (g_lat != G_UNKNOWN || g_lon != G_UNKNOWN) {
+
+// Have location but it is posible one part is invalid.
+
+	  if (g_lat != G_UNKNOWN) {
+  
+	    if (g_lat >= 0) {
+	      absll = g_lat;
+	      news = 'N';
+	    }
+	    else {
+	      absll = - g_lat;
+	      news = 'S';
+	    }
+	    deg = (int) absll;
+	    min = (absll - deg) * 60.0;
+	    sprintf (s_lat, "%c %02d%s%07.4f", news, deg, CH_DEGREE, min);
+	  }
+	  else {
+	    strcpy (s_lat, "Invalid Latitude");
+	  }
+
+	  if (g_lon != G_UNKNOWN) {
+
+	    if (g_lon >= 0) {
+	      absll = g_lon;
+	      news = 'E';
+	    }
+	    else {
+	      absll = - g_lon;
+	      news = 'W';
+	    }
+	    deg = (int) absll;
+	    min = (absll - deg) * 60.0;
+	    sprintf (s_lon, "%c %03d%s%07.4f", news, deg, CH_DEGREE, min);
+	  }
+	  else {
+	    strcpy (s_lon, "Invalid Longitude");
+	  }	
+
+	  sprintf (stemp, "%s, %s", s_lat, s_lon);
+	}
+
+	if (g_speed != G_UNKNOWN) {
+	  char spd[20];
+
+	  if (strlen(stemp) > 0) strcat (stemp, ", ");
+	  sprintf (spd, "%.0f MPH", g_speed);
+	  strcat (stemp, spd);
+	};
+
+	if (g_course != G_UNKNOWN) {
+	  char cse[20];
+
+	  if (strlen(stemp) > 0) strcat (stemp, ", ");
+	  sprintf (cse, "course %.0f", g_course);
+	  strcat (stemp, cse);
+	};
+
+	if (g_altitude != G_UNKNOWN) {
+	  char alt[20];
+
+	  if (strlen(stemp) > 0) strcat (stemp, ", ");
+	  sprintf (alt, "alt %.0f ft", g_altitude);
+	  strcat (stemp, alt);
+	};
+
+	if (strlen(g_freq) > 0) {
+	  strcat (stemp, ", ");
+	  strcat (stemp, g_freq);
+	}
+
+
+	if (strlen (stemp) > 0) {
+	  text_color_set(DW_COLOR_DECODED);
+	  dw_printf("%s\n", stemp);
+	}
+
+
+/*
+ * Third line has:
+ * - comment or weather
+ *
+ * Non-printable characters are changed to safe hexadecimal representations.
+ * For example, carriage return is displayed as <0x0d>.
+ *
+ * Drop annoying trailing CR LF.  Anyone who cares can see it in the raw data.
+ */
+
+	n = strlen(g_comment);
+	if (n >= 1 && g_comment[n-1] == '\n') {
+	  g_comment[n-1] = '\0';
+	  n--;
+	}
+	if (n >= 1 && g_comment[n-1] == '\r') {
+	  g_comment[n-1] = '\0';
+	  n--;
+	}
+	if (n > 0) {
+	  int j;
+
+	  ax25_safe_print (g_comment, -1, 0);
+	  dw_printf("\n");
+
+/*
+ * Point out incorrect attempts a degree symbol.
+ * 0xb0 is degree in ISO Latin1.
+ * To be part of a valid UTF-8 sequence, it would need to be preceded by 11xxxxxx or 10xxxxxx.
+ * 0xf8 is degree in Microsoft code page 437.
+ * To be part of a valid UTF-8 sequence, it would need to be followed by 10xxxxxx.
+ */
+	  for (j=0; j<n; j++) {
+	    if ((unsigned)g_comment[j] == (char)0xb0 &&  (j == 0 || ! (g_comment[j-1] & 0x80))) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf("Character code 0xb0 is probably an attempt at a degree symbol.\n");
+	      dw_printf("The correct encoding is 0xc2 0xb0 in UTF-8.\n");
+	    }	    	
+	  }
+	  for (j=0; j<n; j++) {
+	    if ((unsigned)g_comment[j] == (char)0xf8 && (j == n-1 || (g_comment[j+1] & 0xc0) != 0xc0)) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf("Character code 0xf8 is probably an attempt at a degree symbol.\n");
+	      dw_printf("The correct encoding is 0xc2 0xb0 in UTF-8.\n");	    	
+	    }	
+	  }	
+	}
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_ll_pos
+ *
+ * Purpose:	Decode "Lat/Long Position Report - without Timestamp"
+ *
+ *		Reports without a timestamp can be regarded as real-time.
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	g_lat, g_lon, g_symbol_table, g_symbol_code, g_speed, g_course, g_altitude.
+ *
+ * Description:	Type identifier '=' has APRS messaging.
+ *		Type identifier '!' does not have APRS messaging.
+ *
+ *		The location can be in either compressed or human-readable form.
+ *
+ *		When the symbol code is '_' this is a weather report.
+ *
+ * Examples:	!4309.95NS07307.13W#PHG3320 W2,NY2 Mt Equinox VT k2lm at arrl.net
+ *		!4237.14NS07120.83W#
+ * 		=4246.40N/07115.15W# {UIV32}
+ *
+ *		TODO: (?) Special case, DF report when sym table id = '/' and symbol code = '\'.
+ *
+ * 		=4903.50N/07201.75W\088/036/270/729
+ *
+ *------------------------------------------------------------------*/
+
+static void aprs_ll_pos (unsigned char *info, int ilen) 
+{
+
+	struct aprs_ll_pos_s {
+	  char dti;			/* ! or = */
+	  position_t pos;
+	  char comment[43]; 		/* Start of comment could be data extension(s). */
+	} *p;
+
+	struct aprs_compressed_pos_s {
+	  char dti;			/* ! or = */
+	  compressed_position_t cpos;
+	  char comment[40]; 		/* No data extension allowed for compressed location. */
+	} *q;
+
+
+	strcpy (g_msg_type, "Position");
+
+	p = (struct aprs_ll_pos_s *)info;
+	q = (struct aprs_compressed_pos_s *)info;
+	
+	if (isdigit((unsigned char)(p->pos.lat[0]))) 	/* Human-readable location. */
+        {
+	  decode_position (&(p->pos));
+
+	  if (g_symbol_code == '_') {
+	    /* Symbol code indidates it is a weather report. */
+	    /* In this case, we expect 7 byte "data extension" */
+	    /* for the wind direction and speed. */
+
+	    strcpy (g_msg_type, "Weather Report");
+	    weather_data (p->comment, TRUE);
+	  } 
+	  else {
+	    /* Regular position report. */
+
+	    data_extension_comment (p->comment);
+	  }
+	}
+	else					/* Compressed location. */
+	{
+	  decode_compressed_position (&(q->cpos));
+
+	  if (g_symbol_code == '_') {
+	    /* Symbol code indidates it is a weather report. */
+	    /* In this case, the wind direction and speed are in the */
+	    /* compressed data so we don't expect a 7 byte "data */
+	    /* extension" for them. */
+
+	    strcpy (g_msg_type, "Weather Report");
+	    weather_data (q->comment, FALSE);
+	  } 
+	  else {
+	    /* Regular position report. */
+
+	    process_comment (q->comment, -1);
+	  }
+	}
+
+
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_ll_pos_time
+ *
+ * Purpose:	Decode "Lat/Long Position Report - with Timestamp"
+ *
+ *		Reports sent with a timestamp might contain very old information.
+ *
+ *		Otherwise, same as above.
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	g_lat, g_lon, g_symbol_table, g_symbol_code, g_speed, g_course, g_altitude.
+ *
+ * Description:	Type identifier '@' has APRS messaging.
+ *		Type identifier '/' does not have APRS messaging.
+ *
+ *		The location can be in either compressed or human-readable form.
+ *
+ *		When the symbol code is '_' this is a weather report.
+ *
+ * Examples:	@041025z4232.32N/07058.81W_124/000g000t036r000p000P000b10229h65/wx rpt
+ * 		@281621z4237.55N/07120.20W_017/002g006t022r000p000P000h85b10195.Dvs
+ *		/092345z4903.50N/07201.75W>Test1234
+ *
+ * 		I think the symbol code of "_" indicates weather report.
+ *
+ *		(?) Special case, DF report when sym table id = '/' and symbol code = '\'.
+ *
+ *		@092345z4903.50N/07201.75W\088/036/270/729
+ *		/092345z4903.50N/07201.75W\000/000/270/729
+ *
+ *------------------------------------------------------------------*/
+
+
+
+static void aprs_ll_pos_time (unsigned char *info, int ilen) 
+{
+
+	struct aprs_ll_pos_time_s {
+	  char dti;			/* / or @ */
+	  char time_stamp[7];
+	  position_t pos;
+	  char comment[43]; 		/* First 7 bytes could be data extension. */
+	} *p;
+
+	struct aprs_compressed_pos_time_s {
+	  char dti;			/* / or @ */
+	  char time_stamp[7];
+	  compressed_position_t cpos;
+	  char comment[40]; 		/* No data extension in this case. */
+	} *q;
+
+
+	strcpy (g_msg_type, "Position with time");
+
+	time_t ts = 0;
+
+
+	p = (struct aprs_ll_pos_time_s *)info;
+	q = (struct aprs_compressed_pos_time_s *)info;
+	
+
+	if (isdigit((unsigned char)(p->pos.lat[0]))) 		/* Human-readable location. */
+        {
+	  ts = get_timestamp (p->time_stamp);
+	  decode_position (&(p->pos));
+
+	  if (g_symbol_code == '_') {
+	    /* Symbol code indidates it is a weather report. */
+	    /* In this case, we expect 7 byte "data extension" */
+	    /* for the wind direction and speed. */
+
+	    strcpy (g_msg_type, "Weather Report");
+	    weather_data (p->comment, TRUE);
+	  } 
+	  else {
+	    /* Regular position report. */
+
+	    data_extension_comment (p->comment);
+	  }
+	}
+	else					/* Compressed location. */
+	{
+	  ts = get_timestamp (p->time_stamp);
+
+	  decode_compressed_position (&(q->cpos));
+
+	  if (g_symbol_code == '_') {
+	    /* Symbol code indidates it is a weather report. */
+	    /* In this case, the wind direction and speed are in the */
+	    /* compressed data so we don't expect a 7 byte "data */
+	    /* extension" for them. */
+
+	    strcpy (g_msg_type, "Weather Report");
+	    weather_data (q->comment, FALSE);
+	  } 
+	  else {
+	    /* Regular position report. */
+
+	    process_comment (q->comment, -1);
+	  }
+	}
+
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_raw_nmea
+ *
+ * Purpose:	Decode "Raw NMEA Position Report"
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	??? TBD
+ *
+ * Description:	APRS recognizes raw ASCII data strings conforming to the NMEA 0183
+ *		Version 2.0 specification, originating from navigation equipment such 
+ *		as GPS and LORAN receivers. It is recommended that APRS stations 
+ *		interpret at least the following NMEA Received Sentence types:
+ *
+ *		GGA Global Positioning System Fix Data
+ *		GLL Geographic Position, Latitude/Longitude Data
+ *		RMC Recommended Minimum Specific GPS/Transit Data
+ *		VTG Velocity and Track Data
+ *		WPL Way Point Location
+ *
+ * Examples:	$GPGGA,102705,5157.9762,N,00029.3256,W,1,04,2.0,75.7,M,47.6,M,,*62
+ *		$GPGLL,2554.459,N,08020.187,W,154027.281,A
+ *		$GPRMC,063909,A,3349.4302,N,11700.3721,W,43.022,89.3,291099,13.6,E*52
+ *		$GPVTG,318.7,T,,M,35.1,N,65.0,K*69
+ *
+ *
+ *------------------------------------------------------------------*/
+
+static void nmea_checksum (char *sent)
+{
+        char *p;
+        char *next;
+        unsigned char cs;
+
+
+// Do we have valid checksum?
+
+        cs = 0;
+        for (p = sent+1; *p != '*' && *p != '\0'; p++) {
+          cs ^= *p;
+        }
+
+        p = strchr (sent, '*');
+        if (p == NULL) {
+	  text_color_set (DW_COLOR_INFO);
+          dw_printf("Missing GPS checksum.\n");
+          return;
+        }
+        if (cs != strtoul(p+1, NULL, 16)) {
+	  text_color_set (DW_COLOR_ERROR);
+          dw_printf("GPS checksum error. Expected %02x but found %s.\n", cs, p+1);
+          return;
+        }
+        *p = '\0';      // Remove the checksum.
+}
+
+static void aprs_raw_nmea (unsigned char *info, int ilen) 
+{
+	char stemp[256];
+	char *ptype;
+	char *next;
+
+
+	strcpy (g_msg_type, "Raw NMEA");
+
+	strncpy (stemp, (char *)info, ilen);
+	stemp[ilen] = '\0';
+	nmea_checksum (stemp);
+
+	next = stemp;
+	ptype = strsep(&next, ",");
+
+	if (strcmp(ptype, "$GPGGA") == 0) 
+	{
+	  char *ptime;			/* Time, hhmmss[.sss] */
+	  char *plat;			/* Latitude */
+	  char *pns;			/* North/South */
+	  char *plon;			/* Longitude */
+	  char *pew;			/* East/West */
+	  char *pquality;		/* Fix Quality: 0=invalid, 1=GPS, 2=DGPS */
+	  char *pnsat;			/* Number of satellites. */
+	  char *phdop;			/* Horizontal dilution of precision. */
+	  char *paltitude;		/* Altitude, meters above mean sea level. */
+   	  char *pm;			/* "M" = meters */
+					/* Various other stuff... */
+
+
+	  ptime = strsep(&next, ",");	
+	  plat = strsep(&next, ",");
+	  pns = strsep(&next, ",");
+	  plon = strsep(&next, ",");
+	  pew = strsep(&next, ",");
+	  pquality = strsep(&next, ",");
+	  pnsat = strsep(&next, ",");
+	  phdop = strsep(&next, ",");
+	  paltitude = strsep(&next, ",");
+	  pm = strsep(&next, ",");
+
+	  /* Process time??? */
+
+	  if (plat != NULL && strlen(plat) > 0) {
+	    g_lat = get_latitude_nmea(plat, pns);
+	  }
+	  if (plon != NULL && strlen(plon) > 0) {
+	    g_lon = get_longitude_nmea(plon, pew);
+	  }
+	  if (paltitude != NULL && strlen(paltitude) > 0) {
+	    g_altitude = METERS_TO_FEET(atof(paltitude));
+	  }
+	}
+	else if (strcmp(ptype, "$GPGLL") == 0)
+	{
+	  char *plat;		/* Latitude */
+	  char *pns;		/* North/South */
+	  char *plon;		/* Longitude */
+	  char *pew;		/* East/West */
+				/* optional Time hhmmss[.sss] */
+				/* optional 'A' for data valid */
+
+	  plat = strsep(&next, ",");
+	  pns = strsep(&next, ",");
+	  plon = strsep(&next, ",");
+	  pew = strsep(&next, ",");
+
+	  if (plat != NULL && strlen(plat) > 0) {
+	    g_lat = get_latitude_nmea(plat, pns);
+	  }
+	  if (plon != NULL && strlen(plon) > 0) {
+	    g_lon = get_longitude_nmea(plon, pew);
+	  }
+
+	}
+	else if (strcmp(ptype, "$GPRMC") == 0)
+	{
+	  //char *ptime, *pstatus, *plat, *pns, *plon, *pew, *pspeed, *ptrack, *pdate;
+
+	  char *ptime;			/* Time, hhmmss[.sss] */
+	  char *pstatus;		/* Status, A=Active (valid position), V=Void */
+	  char *plat;			/* Latitude */
+	  char *pns;			/* North/South */
+	  char *plon;			/* Longitude */
+	  char *pew;			/* East/West */
+	  char *pknots;			/* Speed over ground, knots. */
+	  char *pcourse;		/* True course, degrees. */
+	  char *pdate;			/* Date, ddmmyy */
+					/* Magnetic variation */
+					/* In version 3.00, mode is added: A D E N (see below) */
+					/* Checksum */
+
+	  ptime = strsep(&next, ",");
+	  pstatus = strsep(&next, ",");	
+	  plat = strsep(&next, ",");
+	  pns = strsep(&next, ",");
+	  plon = strsep(&next, ",");
+	  pew = strsep(&next, ",");
+	  pknots = strsep(&next, ",");
+	  pcourse = strsep(&next, ",");
+	  pdate = strsep(&next, ",");	
+
+	  /* process time ??? date ??? */
+
+	  if (plat != NULL && strlen(plat) > 0) {
+	    g_lat = get_latitude_nmea(plat, pns);
+	  }
+	  if (plon != NULL && strlen(plon) > 0) {
+	    g_lon = get_longitude_nmea(plon, pew);
+	  }
+	  if (pknots != NULL && strlen(pknots) > 0) {
+	    g_speed = KNOTS_TO_MPH(atof(pknots));
+	  }
+	  if (pcourse != NULL && strlen(pcourse) > 0) {
+	    g_course = atof(pcourse);
+	  }
+	}
+	else if (strcmp(ptype, "$GPVTG") == 0)
+	{
+
+	  /* Speed and direction but NO location! */
+
+	  char *ptcourse;		/* True course, degrees. */
+	  char *pt;			/* "T" */
+	  char *pmcourse;		/* Magnetic course, degrees. */
+	  char *pm;			/* "M" */
+	  char *pknots;			/* Ground speed, knots. */
+	  char *pn;			/* "N" = Knots */
+	  char *pkmh;			/* Ground speed, km/hr */
+	  char *pk;			/* "K" = Kilometers per hour */
+	  char *pmode;			/* New in NMEA 0183 version 3.0 */
+					/* Mode: A=Autonomous, D=Differential, */
+	
+	  ptcourse = strsep(&next, ",");
+	  pt = strsep(&next, ",");
+	  pmcourse = strsep(&next, ",");
+	  pm = strsep(&next, ",");
+	  pknots = strsep(&next, ",");
+	  pn = strsep(&next, ",");
+	  pkmh = strsep(&next, ",");
+	  pk = strsep(&next, ",");
+	  pmode	 = strsep(&next, ",");	
+
+	  if (pknots != NULL && strlen(pknots) > 0) {
+	    g_speed = KNOTS_TO_MPH(atof(pknots));
+	  }
+	  if (ptcourse != NULL && strlen(ptcourse) > 0) {
+	    g_course = atof(ptcourse);
+	  }
+
+	}
+	else if (strcmp(ptype, "$GPWPL") == 0)
+	{
+	  //char *plat, *pns, *plon, *pew, *pident;
+
+	  char *plat;			/* Latitude */
+	  char *pns;			/* North/South */
+	  char *plon;			/* Longitude */
+	  char *pew;			/* East/West */
+	  char *pident;			/* Identifier for Waypoint.  rules??? */
+					/* checksum */
+
+	  plat = strsep(&next, ",");
+	  pns = strsep(&next, ",");
+	  plon = strsep(&next, ",");
+	  pew = strsep(&next, ",");
+	  pident = strsep(&next, ",");
+
+	  if (plat != NULL && strlen(plat) > 0) {
+	    g_lat = get_latitude_nmea(plat, pns);
+	  }
+	  if (plon != NULL && strlen(plon) > 0) {
+	    g_lon = get_longitude_nmea(plon, pew);
+	  }
+
+	  /* do something with identifier? */
+
+	}
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_mic_e
+ *
+ * Purpose:	Decode MIC-E (also Kenwood D7 & D700) packet.
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	
+ *
+ * Description:	
+ *
+ *		Destination Address Field � 
+ *
+ *		The 7-byte Destination Address field contains
+ *		the following encoded information:
+ *
+ *		* The 6 latitude digits.
+ *		* A 3-bit Mic-E message identifier, specifying one of 7 Standard Mic-E
+ *		   Message Codes or one of 7 Custom Message Codes or an Emergency
+ *		   Message Code.
+ *		* The North/South and West/East Indicators.
+ *		* The Longitude Offset Indicator.
+ *		* The generic APRS digipeater path code.
+ *
+ *		"Although the destination address appears to be quite unconventional, it is
+ *		still a valid AX.25 address, consisting only of printable 7-bit ASCII values."
+ *
+ * References:	Mic-E TYPE CODES -- http://www.aprs.org/aprs12/mic-e-types.txt
+ *
+ *		Mic-E TEST EXAMPLES -- http://www.aprs.org/aprs12/mic-e-examples.txt 
+ *		
+ * Examples:	`b9Z!4y>/>"4N}Paul's_TH-D7
+ *
+ * TODO:	Destination SSID can contain generic digipeater path.
+ *
+ * Bugs:	Doesn't handle ambiguous position.  "space" treated as zero.
+ *		Invalid data results in a message but latitude is not set to unknown.
+ *
+ *------------------------------------------------------------------*/
+
+static int mic_e_digit (char c, int mask, int *std_msg, int *cust_msg)
+{
+
+ 	if (c >= '0' && c <= '9') {
+	  return (c - '0');
+	}
+
+	if (c >= 'A' && c <= 'J') {
+	  *cust_msg |= mask;
+	  return (c - 'A');
+	}
+
+	if (c >= 'P' && c <= 'Y') {
+	  *std_msg |= mask;
+	  return (c - 'P');
+	}
+
+	/* K, L, Z should be converted to space. */
+	/* others are invalid. */
+	/* But caller expects only values 0 - 9. */
+
+	if (c == 'K') {
+	  *cust_msg |= mask;
+	  return (0);
+	}
+
+	if (c == 'L') {
+	  return (0);
+	}
+
+	if (c == 'Z') {
+	  *std_msg |= mask;
+	  return (0);
+	}
+
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf("Invalid character \"%c\" in MIC-E destination/latitude.\n", c);
+
+	return (0);
+}
+
+
+static void aprs_mic_e (packet_t pp, unsigned char *info, int ilen) 
+{
+	struct aprs_mic_e_s {
+	  char dti;			/* ' or ` */
+	  unsigned char lon[3];		/* "d+28", "m+28", "h+28" */
+	  unsigned char speed_course[3];		
+	  char symbol_code;
+	  char sym_table_id;
+	} *p;
+
+	char dest[10];
+	int ch;
+	int n;
+	int offset;
+	int std_msg = 0;
+	int cust_msg = 0;
+	const char *std_text[8] = {"Emergency", "Priority", "Special", "Committed", "Returning", "In Service", "En Route", "Off Duty" };
+	const char *cust_text[8] = {"Emergency", "Custom-6", "Custom-5", "Custom-4", "Custom-3", "Custom-2", "Custom-1", "Custom-0" }; 
+	unsigned char *pfirst, *plast;
+
+	strcpy (g_msg_type, "MIC-E");
+
+	p = (struct aprs_mic_e_s *)info;
+
+/* Destination is really latitude of form ddmmhh. */
+/* Message codes are buried in the first 3 digits. */
+
+	ax25_get_addr_with_ssid (pp, AX25_DESTINATION, dest);
+
+	g_lat = mic_e_digit(dest[0], 4, &std_msg, &cust_msg) * 10 + 
+		mic_e_digit(dest[1], 2, &std_msg, &cust_msg) +
+		(mic_e_digit(dest[2], 1, &std_msg, &cust_msg) * 1000 + 
+		 mic_e_digit(dest[3], 0, &std_msg, &cust_msg) * 100 + 
+		 mic_e_digit(dest[4], 0, &std_msg, &cust_msg) * 10 + 
+		 mic_e_digit(dest[5], 0, &std_msg, &cust_msg)) / 6000.0;
+
+
+/* 4th character of desination indicates north / south. */
+
+	if ((dest[3] >= '0' && dest[3] <= '9') || dest[3] == 'L') {
+	  /* South */
+	  g_lat = ( - g_lat);
+	}
+	else if (dest[3] >= 'P' && dest[3] <= 'Z') 
+	{
+	  /* North */
+	}
+	else 
+	{
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid MIC-E N/S encoding in 4th character of destination.\n");	  
+	}
+
+
+/* Longitude is mostly packed into 3 bytes of message but */
+/* has a couple bits of information in the destination. */
+
+	if ((dest[4] >= '0' && dest[4] <= '9') || dest[4] == 'L') 
+	{
+	  offset = 0;
+	}
+	else if (dest[4] >= 'P' && dest[4] <= 'Z') 
+	{
+	  offset = 1;
+	}
+	else 
+	{
+	  offset = 0;
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid MIC-E Longitude Offset in 5th character of destination.\n");
+	}
+
+/* First character of information field is longitude in degrees. */
+/* It is possible for the unprintable DEL character to occur here. */
+
+/* 5th character of desination indicates longitude offset of +100. */
+/* Not quite that simple :-( */
+
+	ch = p->lon[0];
+
+	if (offset && ch >= 118 && ch <= 127) 
+	{
+	    g_lon = ch - 118;			/* 0 - 9 degrees */
+	}
+	else if ( ! offset && ch >= 38 && ch <= 127)
+	{
+	    g_lon = (ch - 38) + 10;		/* 10 - 99 degrees */
+	}
+	else if (offset && ch >= 108 && ch <= 117)
+	{
+	    g_lon = (ch - 108) + 100;		/* 100 - 109 degrees */
+	}
+	else if (offset && ch >= 38 && ch <= 107)
+	{
+	    g_lon = (ch - 38) + 110;		/* 110 - 179 degrees */
+	}
+	else 
+	{
+	    g_lon = G_UNKNOWN;
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("Invalid character 0x%02x for MIC-E Longitude Degrees.\n", ch);
+	}
+
+/* Second character of information field is g_longitude minutes. */
+/* These are all printable characters. */
+
+/* 
+ * More than once I've see the TH-D72A put <0x1a> here and flip between north and south.
+ *
+ * WB2OSZ>TRSW1R,WIDE1-1,WIDE2-2:`c0ol!O[/>=<0x0d>
+ * N 42 37.1200, W 071 20.8300, 0 MPH, course 151
+ *
+ * WB2OSZ>TRS7QR,WIDE1-1,WIDE2-2:`v<0x1a>n<0x1c>"P[/>=<0x0d>
+ * Invalid character 0x1a for MIC-E Longitude Minutes.
+ * S 42 37.1200, Invalid Longitude, 0 MPH, course 252
+ *
+ * This was direct over the air with no opportunity for a digipeater
+ * or anything else to corrupt the message.
+ */
+
+	if (g_lon != G_UNKNOWN) 
+	{
+	  ch = p->lon[1];
+
+	  if (ch >= 88 && ch <= 97)
+	  {
+	    g_lon += (ch - 88) / 60.0;	/* 0 - 9 minutes*/
+	  }
+	  else if (ch >= 38 && ch <= 87)
+	  {
+    	    g_lon += ((ch - 38) + 10) / 60.0;	/* 10 - 59 minutes */
+	  }
+	  else {
+	    g_lon = G_UNKNOWN;
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("Invalid character 0x%02x for MIC-E Longitude Minutes.\n", ch);
+	  }
+
+/* Third character of information field is longitude hundredths of minutes. */
+/* There are 100 possible values, from 0 to 99. */
+/* Note that the range includes 4 unprintable control characters and DEL. */
+
+	  if (g_lon != G_UNKNOWN) 
+	  {
+	    ch = p->lon[2];
+
+	    if (ch >= 28 && ch <= 127) 
+	    {
+	      g_lon += ((ch - 28) + 0) / 6000.0;	/* 0 - 99 hundredths of minutes*/
+	    }
+	    else {
+	      g_lon = G_UNKNOWN;
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf("Invalid character 0x%02x for MIC-E Longitude hundredths of Minutes.\n", ch);
+	    }
+	  }
+	}
+
+/* 6th character of destintation indicates east / west. */
+
+	if ((dest[5] >= '0' && dest[5] <= '9') || dest[5] == 'L') {
+	  /* East */
+	}
+	else if (dest[5] >= 'P' && dest[5] <= 'Z') 
+	{
+	  /* West */
+	  if (g_lon != G_UNKNOWN) {
+	    g_lon = ( - g_lon);
+	  }
+	}
+	else 
+	{
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid MIC-E E/W encoding in 6th character of destination.\n");	  
+	}
+
+/* Symbol table and codes like everyone else. */
+
+	g_symbol_table = p->sym_table_id;
+	g_symbol_code = p->symbol_code;
+
+	if (g_symbol_table != '/' && g_symbol_table != '\\' 
+		&& ! isupper(g_symbol_table) && ! isdigit(g_symbol_table))
+	{
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid symbol table code not one of / \\ A-Z 0-9\n");	
+	  g_symbol_table = '/';
+	}
+
+/* Message type from two 3-bit codes. */
+
+	if (std_msg == 0 && cust_msg == 0) {
+	  strcpy (g_mic_e_status, "Emergency");
+	}
+	else if (std_msg == 0 && cust_msg != 0) {
+	  strcpy (g_mic_e_status, cust_text[cust_msg]);
+	}
+	else if (std_msg != 0 && cust_msg == 0) {
+	  strcpy (g_mic_e_status, std_text[std_msg]);
+	}
+	else {
+	  strcpy (g_mic_e_status, "Unknown MIC-E Message Type");
+	}
+
+/* Speed and course from next 3 bytes. */
+
+	n = ((p->speed_course[0] - 28) * 10) + ((p->speed_course[1] - 28) / 10);
+	if (n >= 800) n -= 800;
+
+	g_speed = KNOTS_TO_MPH(n); 
+
+	n = ((p->speed_course[1] - 28) % 10) * 100 + (p->speed_course[2] - 28);
+	if (n >= 400) n -= 400;
+
+	/* Result is 0 for unknown and 1 - 360 where 360 is north. */
+	/* Convert to 0 - 360 and reserved value for unknown. */
+
+	if (n == 0) 
+	  g_course = G_UNKNOWN;
+	else if (n == 360)
+	  g_course = 0;
+	else
+	  g_course = n;
+
+
+/* Now try to pick out manufacturer and other optional items. */
+/* The telemetry field, in the original spec, is no longer used. */
+  
+	pfirst = info + sizeof(struct aprs_mic_e_s);
+	plast = info + ilen - 1;
+
+/* Carriage return character at the end is not mentioned in spec. */
+/* Remove if found because it messes up extraction of manufacturer. */
+
+	if (*plast == '\r') plast--;
+
+	if (*pfirst == ' ' || *pfirst == '>' || *pfirst == ']' || *pfirst == '`' || *pfirst == '\'') {
+	
+	  if (*pfirst == ' ') { strcpy (g_mfr, "Original MIC-E"); pfirst++; }
+
+	  else if (*pfirst == '>' && *plast == '=') { strcpy (g_mfr, "Kenwood TH-D72"); pfirst++; plast--; }
+	  else if (*pfirst == '>') { strcpy (g_mfr, "Kenwood TH-D7A"); pfirst++; }
+
+	  else if (*pfirst == ']' && *plast == '=') { strcpy (g_mfr, "Kenwood TM-D710"); pfirst++; plast--; }
+	  else if (*pfirst == ']') { strcpy (g_mfr, "Kenwood TM-D700"); pfirst++; }
+
+	  else if (*pfirst == '`' && *(plast-1) == '_' && *plast == ' ') { strcpy (g_mfr, "Yaesu VX-8"); pfirst++; plast-=2; }
+	  else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '"') { strcpy (g_mfr, "Yaesu FTM-350"); pfirst++; plast-=2; }
+	  else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '#') { strcpy (g_mfr, "Yaesu VX-8G"); pfirst++; plast-=2; }
+	  else if (*pfirst == '\'' && *(plast-1) == '|' && *plast == '3') { strcpy (g_mfr, "Byonics TinyTrack3"); pfirst++; plast-=2; }
+	  else if (*pfirst == '\'' && *(plast-1) == '|' && *plast == '4') { strcpy (g_mfr, "Byonics TinyTrack4"); pfirst++; plast-=2; }
+
+	  else if (*(plast-1) == '\\') { strcpy (g_mfr, "Hamhud ?"); pfirst++; plast-=2; }
+	  else if (*(plast-1) == '/') { strcpy (g_mfr, "Argent ?"); pfirst++; plast-=2; }
+	  else if (*(plast-1) == '^') { strcpy (g_mfr, "HinzTec anyfrog"); pfirst++; plast-=2; }
+	  else if (*(plast-1) == '~') { strcpy (g_mfr, "OTHER"); pfirst++; plast-=2; }
+
+	  else if (*pfirst == '`') { strcpy (g_mfr, "Mic-Emsg"); pfirst++; plast-=2; }
+	  else if (*pfirst == '\'') { strcpy (g_mfr, "McTrackr"); pfirst++; plast-=2; }
+	}
+
+/*
+ * An optional altitude is next.
+ * It is three base-91 digits followed by "}".
+ * The TM-D710A might have encoding bug.  This was observed:
+ *
+ * KJ4ETP-9>SUUP9Q,KE4OTZ-3,WIDE1*,WIDE2-1,qAR,KI4HDU-2:`oV$n6:>/]"7&}162.475MHz <Knox,TN> clintserman at gmail=
+ * N 35 50.9100, W 083 58.0800, 25 MPH, course 230, alt 945 ft, 162.475MHz
+ *
+ * KJ4ETP-9>SUUP6Y,GRNTOP-3*,WIDE2-1,qAR,KI4HDU-2:`oU~nT >/]<0x9a>xt}162.475MHz <Knox,TN> clintserman at gmail=
+ * Invalid character in MIC-E altitude.  Must be in range of '!' to '{'.
+ * N 35 50.6900, W 083 57.9800, 29 MPH, course 204, alt 3280843 ft, 162.475MHz
+ *
+ * KJ4ETP-9>SUUP6Y,N4NEQ-3,K4EGA-1,WIDE2*,qAS,N5CWH-1:`oU~nT >/]?xt}162.475MHz <Knox,TN> clintserman at gmail=
+ * N 35 50.6900, W 083 57.9800, 29 MPH, course 204, alt 808497 ft, 162.475MHz
+ *
+ * KJ4ETP-9>SUUP2W,KE4OTZ-3,WIDE1*,WIDE2-1,qAR,KI4HDU-2:`oV2o"J>/]"7)}162.475MHz <Knox,TN> clintserman at gmail=
+ * N 35 50.2700, W 083 58.2200, 35 MPH, course 246, alt 955 ft, 162.475MHz
+ * 
+ * Note the <0x9a> which is outside of the 7-bit ASCII range.  Clearly very wrong.
+ */
+
+	if (plast > pfirst && pfirst[3] == '}') {
+
+	  g_altitude = METERS_TO_FEET((pfirst[0]-33)*91*91 + (pfirst[1]-33)*91 + (pfirst[2]-33) - 10000);
+
+	  if (pfirst[0] < '!' || pfirst[0] > '{' ||
+	      pfirst[1] < '!' || pfirst[1] > '{' ||
+	      pfirst[2] < '!' || pfirst[2] > '{' ) 
+	  {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("Invalid character in MIC-E altitude.  Must be in range of '!' to '{'.\n");
+	    dw_printf("Bogus altitude of %.0f changed to unknown.\n", g_altitude);
+	    g_altitude = G_UNKNOWN;
+	  }
+	  
+	  pfirst += 4;
+	}
+
+	process_comment ((char*)pfirst, (int)(plast - pfirst) + 1);
+
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_message
+ *
+ * Purpose:	Decode "Message Format"
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	??? TBD
+ *
+ * Description:	An APRS message is a text string with a specifed addressee.
+ *
+ *		It's a lot more complicated with different types of addressees
+ *		and replies with acknowledgement or rejection.
+ *
+ *		Displaying and logging these messages could be useful.
+ *
+ * Examples:	
+ *		
+ *
+ *------------------------------------------------------------------*/
+
+static void aprs_message (unsigned char *info, int ilen) 
+{
+
+	struct aprs_message_s {
+	  char dti;			/* : */
+	  char addressee[9];
+	  char colon;			/* : */
+	  char message[73];		/* 0-67 characters for message */
+					/* { followed by 1-5 characters for message number */
+	} *p;
+
+
+	p = (struct aprs_message_s *)info;
+
+	sprintf (g_msg_type, "APRS Message for \"%9.9s\"", p->addressee);
+
+	/* No location so don't use  process_comment () */
+
+	strcpy (g_comment, p->message);
+
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_object
+ *
+ * Purpose:	Decode "Object Report Format"
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	g_object_name, g_lat, g_lon, g_symbol_table, g_symbol_code, g_speed, g_course, g_altitude.
+ *
+ * Description:	Message has a 9 character object name which could be quite different than
+ *		the source station.
+ *
+ *		This can also be a weather report when the symbol id is '_'.
+ *
+ * Examples:	;WA2PNU   *050457z4051.72N/07325.53W]BBS & FlexNet 145.070 MHz
+ *
+ *		;ActonEOC *070352z4229.20N/07125.95WoFire, EMS, Police, Heli-pad, Dial 911
+ *
+ *		;IRLPC494@*012112zI9*n*<ONV0   446325-146IDLE<CR>
+ *
+ *------------------------------------------------------------------*/
+
+static void aprs_object (unsigned char *info, int ilen) 
+{
+
+	struct aprs_object_s {
+	  char dti;			/* ; */
+	  char name[9];
+	  char live_killed;		/* * for live or _ for killed */
+	  char time_stamp[7];
+	  position_t pos;
+	  char comment[43]; 		/* First 7 bytes could be data extension. */
+	} *p;
+
+	struct aprs_compressed_object_s {
+	  char dti;			/* ; */
+	  char name[9];
+	  char live_killed;		/* * for live or _ for killed */
+	  char time_stamp[7];
+	  compressed_position_t cpos;
+	  char comment[40]; 		/* No data extension in this case. */
+	} *q;
+
+
+	time_t ts = 0;
+	int i;
+
+
+	p = (struct aprs_object_s *)info;
+	q = (struct aprs_compressed_object_s *)info;
+
+	strncpy (g_name, p->name, 9);
+	g_name[9] = '\0';
+	i = strlen(g_name) - 1;
+	while (i >= 0 && g_name[i] == ' ') {
+	  g_name[i--] = '\0';
+	}
+
+	if (p->live_killed == '*')
+	  strcpy (g_msg_type, "Object");
+	else if (p->live_killed == '_')
+	  strcpy (g_msg_type, "Killed Object");
+	else
+	  strcpy (g_msg_type, "Object - invalid live/killed");
+
+	ts = get_timestamp (p->time_stamp);
+
+	if (isdigit((unsigned char)(p->pos.lat[0]))) 	/* Human-readable location. */
+        {
+	  decode_position (&(p->pos));
+
+	  if (g_symbol_code == '_') {
+	    /* Symbol code indidates it is a weather report. */
+	    /* In this case, we expect 7 byte "data extension" */
+	    /* for the wind direction and speed. */
+
+	    strcpy (g_msg_type, "Weather Report with Object");
+	    weather_data (p->comment, TRUE);
+	  } 
+	  else {
+	    /* Regular object. */
+
+	    data_extension_comment (p->comment);
+	  }
+	}
+	else					/* Compressed location. */
+	{
+	  decode_compressed_position (&(q->cpos));
+
+	  if (g_symbol_code == '_') {
+	    /* Symbol code indidates it is a weather report. */
+	    /* The spec doesn't explicitly mention the combination */
+	    /* of weather report and object with compressed */
+	    /* position. */
+
+	    strcpy (g_msg_type, "Weather Report with Object");
+	    weather_data (q->comment, FALSE);
+	  } 
+	  else {
+	    /* Regular position report. */
+
+	    process_comment (q->comment, -1);
+	  }
+	}
+
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_item
+ *
+ * Purpose:	Decode "Item Report Format"
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	g_object_name, g_lat, g_lon, g_symbol_table, g_symbol_code, g_speed, g_course, g_altitude.
+ *
+ * Description:	An "item" is very much like an "object" except 
+ *
+ *		-- It doesn't have a time.
+ *		-- Name is a VARIABLE length 3 to 9 instead of fixed 9.
+ *		-- "live" indicator is ! rather than *
+ *
+ * Examples:	
+ *
+ *------------------------------------------------------------------*/
+
+static void aprs_item (unsigned char *info, int ilen) 
+{
+
+	struct aprs_item_s {
+	  char dti;			/* ) */
+	  char name[9];			/* Actually variable length 3 - 9 bytes. */
+	  char live_killed;		/* ! for live or _ for killed */
+	  position_t pos;
+	  char comment[43]; 		/* First 7 bytes could be data extension. */
+	} *p;
+
+	struct aprs_compressed_item_s {
+	  char dti;			/* ) */
+	  char name[9];			/* Actually variable length 3 - 9 bytes. */
+	  char live_killed;		/* ! for live or _ for killed */
+	  compressed_position_t cpos;
+	  char comment[40]; 		/* No data extension in this case. */
+	} *q;
+
+
+	time_t ts = 0;
+	int i;
+	char *ppos;
+
+
+	p = (struct aprs_item_s *)info;
+	q = (struct aprs_compressed_item_s *)info;
+
+	i = 0;
+	while (i < 9 && p->name[i] != '!' && p->name[i] != '_') {
+	  g_name[i] = p->name[i];
+	  i++;
+	  g_name[i] = '\0';
+	}
+
+	if (p->name[i] == '!')
+	  strcpy (g_msg_type, "Item");
+	else if (p->name[i] == '_')
+	  strcpy (g_msg_type, "Killed Item");
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Item name too long or not followed by ! or _.\n");
+	  strcpy (g_msg_type, "Object - invalid live/killed");
+	}
+
+	ppos = p->name + i + 1;
+ 
+	if (isdigit(*ppos)) 		/* Human-readable location. */
+        {
+	  decode_position ((position_t*) ppos);
+
+	  data_extension_comment (ppos + sizeof(position_t));
+	}
+	else					/* Compressed location. */
+	{
+	  decode_compressed_position ((compressed_position_t*)ppos);
+
+	  process_comment (ppos + sizeof(compressed_position_t), -1);
+	}
+
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_station_capabilities
+ *
+ * Purpose:	Decode "Station Capabilities"
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	???
+ *
+ * Description:	Each capability is a TOKEN or TOKEN=VALUE pair.
+ *
+ *
+ * Example:	<IGATE,MSG_CNT=3,LOC_CNT=49<CR>
+ *		
+ * Bugs:	Not implemented yet.  Treat whole thing as comment.	
+ *
+ *------------------------------------------------------------------*/
+
+static void aprs_station_capabilities (char *info, int ilen) 
+{
+
+	strcpy (g_msg_type, "Station Capabilities");
+
+	// 	Is process_comment() applicable?
+
+	strcpy (g_comment, info+1);
+}
+
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_status_report
+ *
+ * Purpose:	Decode "Status Report"
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	???
+ *
+ * Description:	There are 3 different formats:
+ *
+ *		(1)	'>'
+ *			7 char - timestamp, DHM z format
+ *			0-55 char - status text
+ *
+ *		(3)	'>'
+ *			4 or 6 char - Maidenhead Locator
+ *			2 char - symbol table & code
+ *			' ' character
+ *			0-53 char - status text	
+ *
+ *		(2)	'>'
+ *			0-62 char - status text
+ *
+ *		
+ *		In all cases, Beam heading and ERP can be at the
+ *		very end by using '^' and two other characters.
+ *		
+ *
+ * Examples from specification:	
+ *		
+ *
+ *		>Net Control Center without timestamp.
+ *		>092345zNet Control Center with timestamp.
+ *		>IO91SX/G
+ *		>IO91/G
+ *		>IO91SX/- My house 		(Note the space at the start of the status text).
+ *		>IO91SX/- ^B7 			Meteor Scatter beam heading = 110 degrees, ERP = 490 watts.
+ *	
+ *------------------------------------------------------------------*/
+
+static void aprs_status_report (char *info, int ilen) 
+{
+	struct aprs_status_time_s {
+	  char dti;			/* > */
+	  char ztime[7];		/* Time stamp ddhhmmz */
+	  char comment[55]; 		
+	} *pt;
+
+	struct aprs_status_m4_s {
+	  char dti;			/* > */
+	  char mhead4[4];		/* 4 character Maidenhead locator. */
+	  char sym_table_id;
+	  char symbol_code;
+	  char space;			/* Should be space after symbol code. */
+	  char comment[54]; 		
+	} *pm4;
+
+	struct aprs_status_m6_s {
+	  char dti;			/* > */
+	  char mhead6[6];		/* 6 character Maidenhead locator. */
+	  char sym_table_id;
+	  char symbol_code;
+	  char space;			/* Should be space after symbol code. */
+	  char comment[54]; 		
+	} *pm6;
+
+	struct aprs_status_s {
+	  char dti;			/* > */
+	  char comment[62]; 		
+	} *ps;
+
+
+	strcpy (g_msg_type, "Status Report");
+
+	pt = (struct aprs_status_time_s *)info;
+	pm4 = (struct aprs_status_m4_s *)info;
+	pm6 = (struct aprs_status_m6_s *)info;
+	ps = (struct aprs_status_s *)info;
+
+/*
+ * Do we have format with time?
+ */
+	if (isdigit(pt->ztime[0]) &&
+	    isdigit(pt->ztime[1]) &&
+	    isdigit(pt->ztime[2]) &&
+	    isdigit(pt->ztime[3]) &&
+	    isdigit(pt->ztime[4]) &&
+	    isdigit(pt->ztime[5]) &&
+	    pt->ztime[6] == 'z') {
+
+	  strcpy (g_comment, pt->comment);
+	}
+
+/*
+ * Do we have format with 6 character Maidenhead locator?
+ */
+	else if (get_maidenhead (pm6->mhead6) == 6) {
+
+	  strncpy (g_maidenhead, pm6->mhead6, 6);
+	  g_maidenhead[6] = '\0';
+
+	  g_symbol_table = pm6->sym_table_id;
+	  g_symbol_code = pm6->symbol_code;
+
+	  if (g_symbol_table != '/' && g_symbol_table != '\\' 
+		&& ! isupper(g_symbol_table) && ! isdigit(g_symbol_table))
+	  {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("Invalid symbol table code '%c' not one of / \\ A-Z 0-9\n", g_symbol_table);	
+	    g_symbol_table = '/';
+	  }
+
+	  if (pm6->space != ' ' && pm6->space != '\0') {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("Error: Found '%c' instead of space required after symbol code.\n", pm6->space);	
+	  }
+
+	  strcpy (g_comment, pm6->comment);
+	}
+
+/*
+ * Do we have format with 4 character Maidenhead locator?
+ */
+	else if (get_maidenhead (pm4->mhead4) == 4) {
+
+	  strncpy (g_maidenhead, pm4->mhead4, 4);
+	  g_maidenhead[4] = '\0';
+
+	  g_symbol_table = pm4->sym_table_id;
+	  g_symbol_code = pm4->symbol_code;
+
+	  if (g_symbol_table != '/' && g_symbol_table != '\\' 
+		&& ! isupper(g_symbol_table) && ! isdigit(g_symbol_table))
+	  {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("Invalid symbol table code '%c' not one of / \\ A-Z 0-9\n", g_symbol_table);	
+	    g_symbol_table = '/';
+	  }
+
+	  if (pm4->space != ' ' && pm4->space != '\0') {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("Error: Found '%c' instead of space required after symbol code.\n", pm4->space);	
+	  }
+
+	  strcpy (g_comment, pm4->comment);
+	}
+
+/*
+ * Whole thing is status text.
+ */
+	else {
+	  strcpy (g_comment, ps->comment);
+	}
+
+
+/*
+ * Last 3 characters can represent beam heading and ERP.
+ */
+
+	if (strlen(g_comment) >= 3) {
+	  char *hp = g_comment + strlen(g_comment) - 3;
+	
+	  if (*hp == '^') {
+
+	    char h = hp[1];
+	    char p = hp[2];
+	    int beam = -1;
+	    int erp = -1;
+
+	    if (h >= '0' && h <= '9') {
+	      beam = (h - '0') * 10;
+	    }
+	    else if (h >= 'A' && h <= 'Z') {
+	      beam = (h - 'A') * 10 + 100;
+	    }
+
+	    if (p >= '1' && p <= 'K') {
+	      erp = (p - '0') * (p - '0') * 10;
+	    }
+
+	// TODO:  put result somewhere.
+	// could use g_directivity and need new variable for erp.
+
+	    *hp = '\0';
+	  }
+	}
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_Telemetry
+ *
+ * Purpose:	Decode "Telemetry"
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	???
+ *
+ * Description:	TBD.
+ *
+ * Examples from specification:	
+ *		
+ *
+ *		TBD
+ *	
+ *------------------------------------------------------------------*/
+
+static void aprs_telemetry (char *info, int ilen) 
+{
+
+	strcpy (g_msg_type, "Telemetry");
+
+	/* It's pretty much human readable already. */
+	/* Just copy the info field. */
+
+	strcpy (g_comment, info);
+
+
+} /* end aprs_telemetry */
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_raw_touch_tone
+ *
+ * Purpose:	Decode raw touch tone data.
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Description:	Touch tone data is converted to a packet format
+ *		so it can be conveyed to an application for processing.
+ *
+ * 		This is not part of the APRS standard.	
+ *		
+ *------------------------------------------------------------------*/
+
+static void aprs_raw_touch_tone (char *info, int ilen) 
+{
+
+	strcpy (g_msg_type, "Raw Touch Tone Data");
+
+	/* Just copy the info field without the message type. */
+
+	if (*info == '{') 
+	  strcpy (g_comment, info+3);
+	else
+	  strcpy (g_comment, info+1);
+
+
+} /* end aprs_raw_touch_tone */
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_morse_code
+ *
+ * Purpose:	Convey message in packet format to be transmitted as 
+ *		Morse Code.
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Description:	This is not part of the APRS standard.	
+ *		
+ *------------------------------------------------------------------*/
+
+static void aprs_morse_code (char *info, int ilen) 
+{
+
+	strcpy (g_msg_type, "Morse Code Data");
+
+	/* Just copy the info field without the message type. */
+
+	if (*info == '{') 
+	  strcpy (g_comment, info+3);
+	else
+	  strcpy (g_comment, info+1);
+
+
+} /* end aprs_morse_code */
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_ll_pos_time
+ *
+ * Purpose:	Decode weather report without a position.
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	g_symbol_table, g_symbol_code.
+ *
+ * Description:	Type identifier '_' is a weather report without a position.
+ *
+ *------------------------------------------------------------------*/
+
+
+
+static void aprs_positionless_weather_report (unsigned char *info, int ilen) 
+{
+
+	struct aprs_positionless_weather_s {
+	  char dti;			/* _ */
+	  char time_stamp[8];		/* MDHM format */
+	  char comment[99]; 		
+	} *p;
+
+
+	strcpy (g_msg_type, "Positionless Weather Report");
+
+	time_t ts = 0;
+
+
+	p = (struct aprs_positionless_weather_s *)info;
+	
+	// not yet implemented for 8 character format // ts = get_timestamp (p->time_stamp);
+
+	weather_data (p->comment, FALSE);
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	weather_data
+ *
+ * Purpose:	Decode weather data in position or object report.
+ *
+ * Inputs:	info 	- Pointer to first byte after location
+ *			  and symbol code.
+ *
+ *		wind_prefix 	- Expecting leading wind info
+ *				  for human-readable location.
+ *				  (Currently ignored.  We are very
+ *				  forgiving in what is accepted.)
+ * TODO: call this context instead and have 3 enumerated values.
+ *
+ * Global In:	g_course	- Wind info for compressed location.
+ *		g_speed
+ *
+ * Outputs:	g_comment
+ *
+ * Description:	Extract weather details and format into a comment.
+ *
+ *		For human-readable locations, we expect wind direction
+ *		and speed in a format like this:  999/999.
+ *		For compressed location, this has already been 
+ * 		processed and put in g_course and g_speed.
+ *		Otherwise, for positionless weather data, the 
+ *		wind is in the form c999s999.
+ *
+ * References:	APRS Weather specification comments.
+ *		http://aprs.org/aprs11/spec-wx.txt
+ *
+ *		Weather updates to the spec.
+ *		http://aprs.org/aprs12/weather-new.txt
+ *
+ * Examples:
+ *	
+ *	_10090556c220s004g005t077r000p000P000h50b09900wRSW
+ *	!4903.50N/07201.75W_220/004g005t077r000p000P000h50b09900wRSW
+ *	!4903.50N/07201.75W_220/004g005t077r000p000P000h50b.....wRSW
+ *	@092345z4903.50N/07201.75W_220/004g005t-07r000p000P000h50b09900wRSW
+ *	=/5L!!<*e7_7P[g005t077r000p000P000h50b09900wRSW
+ *	@092345z/5L!!<*e7_7P[g005t077r000p000P000h50b09900wRSW
+ *	;BRENDA   *092345z4903.50N/07201.75W_220/004g005b0990
+ *
+ *------------------------------------------------------------------*/
+
+static int getwdata (char **wpp, char ch, int dlen, float *val) 
+{
+	char stemp[8];
+	int i;
+
+
+	//dw_printf("debug: getwdata (wp=%p, ch=%c, dlen=%d)\n", *wpp, ch, dlen);
+
+	*val = G_UNKNOWN;
+
+	assert (dlen >= 2 && dlen <= 6);
+
+	if (**wpp != ch) {
+	  /* Not specified element identifier. */
+	  return (0);	
+	}
+	
+	if (strncmp((*wpp)+1, "......", dlen) == 0 || strncmp((*wpp)+1, "      ", dlen) == 0) {
+	  /* Field present, unknown value */
+	  *wpp += 1 + dlen;
+	  return (1); 
+	}
+
+	/* Data field can contain digits, decimal point, leading negative. */
+
+	for (i=1; i<=dlen; i++) {
+	  if ( ! isdigit((*wpp)[i]) && (*wpp)[i] != '.' && (*wpp)[i] != '-' ) {
+	    return(0);
+	  }
+	} 
+
+	strncpy (stemp, (*wpp)+1, dlen);
+	stemp[dlen] = '\0';
+	*val = atof(stemp);
+
+	//dw_printf("debug: getwdata returning %f\n", *val);
+
+	*wpp += 1 + dlen;
+	return (1); 
+}	
+
+static void weather_data (char *wdata, int wind_prefix) 
+{
+	int n;
+	float fval;
+	char *wp = wdata;
+	int keep_going;
+
+	
+	if (wp[3] == '/')
+	{
+	  if (sscanf (wp, "%3d", &n))
+	  {
+	    // Data Extension format.
+	    // Fine point:  Officially, should be values of 001-360.
+	    // "000" or "..." or "   " means unknown. 
+	    // In practice we see do see "000" here.
+	    g_course = n;
+	  }
+	  if (sscanf (wp+4, "%3d", &n))
+	  {
+	    g_speed = KNOTS_TO_MPH(n);  /* yes, in knots */
+	  }
+	  wp += 7;
+	}
+	else if ( g_speed == G_UNKNOWN) {
+
+	  if ( ! getwdata (&wp, 'c', 3, &g_course)) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("Didn't find wind direction in form c999.\n");
+	  }
+	  if ( ! getwdata (&wp, 's', 3, &g_speed)) {	/* MPH here */
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("Didn't find wind speed in form s999.\n");
+	  }
+	}
+
+// At this point, we should have the wind direction and speed
+// from one of three methods.
+
+	if (g_speed != G_UNKNOWN) {
+	  char ctemp[30];
+
+	  sprintf (g_comment, "wind %.1f mph", g_speed);
+	  if (g_course != G_UNKNOWN) {
+	    sprintf (ctemp, ", direction %.0f", g_course);
+	    strcat (g_comment, ctemp);
+	  }
+	}
+
+	/* We don't want this to show up on the location line. */
+	g_speed = G_UNKNOWN;
+	g_course = G_UNKNOWN;
+
+/*
+ * After the mandatory wind direction and speed (in 1 of 3 formats), the
+ * next two must be in fixed positions:
+ * - gust (peak in mph last 5 minutes)
+ * - temperature, degrees F, can be negative e.g. -01
+ */
+	if (getwdata (&wp, 'g', 3, &fval)) {
+	  if (fval != G_UNKNOWN) {
+	    char ctemp[30];
+	    sprintf (ctemp, ", gust %.0f", fval);
+	    strcat (g_comment, ctemp);
+	  }
+	}
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Didn't find wind gust in form g999.\n");
+	}
+
+	if (getwdata (&wp, 't', 3, &fval)) {
+	  if (fval != G_UNKNOWN) {
+	    char ctemp[30];
+	    sprintf (ctemp, ", temperature %.0f", fval);
+	    strcat (g_comment, ctemp);
+	  }
+	}
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Didn't find temperature in form t999.\n");
+	}
+
+/*
+ * Now pick out other optional fields in any order.
+ */
+	keep_going = 1;
+	while (keep_going) {
+
+	  if (getwdata (&wp, 'r', 3, &fval)) {	
+
+	/* r = rainfall, 1/100 inch, last hour */
+
+	    if (fval != G_UNKNOWN) {
+	      char ctemp[30];
+	      sprintf (ctemp, ", rain %.2f in last hour", fval / 100.);
+	      strcat (g_comment, ctemp);
+	    }
+	  }
+	  else if (getwdata (&wp, 'p', 3, &fval)) {	
+
+	/* p = rainfall, 1/100 inch, last 24 hours */
+
+	    if (fval != G_UNKNOWN) {
+	      char ctemp[30];
+	      sprintf (ctemp, ", rain %.2f in last 24 hours", fval / 100.);
+	      strcat (g_comment, ctemp);
+	    }
+	  }
+	  else if (getwdata (&wp, 'P', 3, &fval)) {	
+
+	/* P = rainfall, 1/100 inch, since midnight */
+
+	    if (fval != G_UNKNOWN) {
+	      char ctemp[30];
+	      sprintf (ctemp, ", rain %.2f since midnight", fval / 100.);
+	      strcat (g_comment, ctemp);
+	    }
+	  }
+	  else if (getwdata (&wp, 'h', 2, &fval)) {	
+
+	/* h = humidity %, 00 means 100%  */
+
+	    if (fval != G_UNKNOWN) {
+	      char ctemp[30];
+	      if (fval == 0) fval = 100;
+	      sprintf (ctemp, ", humidity %.0f", fval);
+	      strcat (g_comment, ctemp);
+	    }
+	  }
+	  else if (getwdata (&wp, 'b', 5, &fval)) {	
+
+	/* b = barometric presure (tenths millibars / tenths of hPascal)  */
+	/* Here, display as inches of mercury. */
+
+	    if (fval != G_UNKNOWN) {
+	      char ctemp[30];
+	      fval = MBAR_TO_INHG(fval * 0.1);
+	      sprintf (ctemp, ", barometer %.2f", fval);
+	      strcat (g_comment, ctemp);
+	    }
+	  }
+	  else if (getwdata (&wp, 'L', 3, &fval)) {	
+
+	/* L = Luminosity, watts/ sq meter, 000-999  */
+	
+	    if (fval != G_UNKNOWN) {
+	      char ctemp[30];
+	      sprintf (ctemp, ", %.0f watts/m^2", fval);
+	      strcat (g_comment, ctemp);
+	    }
+	  }
+	  else if (getwdata (&wp, 'l', 3, &fval)) {	
+
+	/* l = Luminosity, watts/ sq meter, 1000-1999  */
+	
+	    if (fval != G_UNKNOWN) {
+	      char ctemp[30];
+	      sprintf (ctemp, ", %.0f watts/m^2", fval + 1000);
+	      strcat (g_comment, ctemp);
+	    }
+	  }
+	  else if (getwdata (&wp, 's', 3, &fval)) {	
+
+	/* s = Snowfall in last 24 hours, inches  */
+	/* Data can have decimal point so we don't have to worry about scaling. */
+	/* 's' is also used by wind speed but that must be in a fixed */
+	/* position in the message so there is no confusion. */
+	
+	    if (fval != G_UNKNOWN) {
+	      char ctemp[30];
+	      sprintf (ctemp, ", %.1f snow in 24 hours", fval);
+	      strcat (g_comment, ctemp);
+	    }
+	  }
+	  else if (getwdata (&wp, 's', 3, &fval)) {	
+
+	/* # = Raw rain counter  */
+	
+	    if (fval != G_UNKNOWN) {
+	      char ctemp[30];
+	      sprintf (ctemp, ", raw rain counter %.f", fval);
+	      strcat (g_comment, ctemp);
+	    }
+	  }
+	  else if (getwdata (&wp, 'X', 3, &fval)) {	
+
+	/* X = Nuclear Radiation.  */
+	/* Encoded as two significant digits and order of magnitude */
+	/* like resistor color code. */
+
+// TODO: decode this properly
+	
+	    if (fval != G_UNKNOWN) {
+	      char ctemp[30];
+	      sprintf (ctemp, ", nuclear Radiation %.f", fval);
+	      strcat (g_comment, ctemp);
+	    }
+	  }
+
+// TODO: add new flood level, battery voltage, etc.
+
+	  else {
+	    keep_going = 0;
+	  }
+	}
+
+/*
+ * We should be left over with:
+ * - one character for software.
+ * - two to four characters for weather station type.
+ * Examples: tU2k, wRSW
+ *
+ * But few people follow the protocol spec here.  Instead more often we see things like:
+ *  sunny/WX
+ *  / {UIV32N}
+ */
+
+	strcat (g_comment, ", \"");
+	strcat (g_comment, wp);
+/*
+ * Drop any CR / LF character at the end.
+ */
+	n = strlen(g_comment);
+	if (n >= 1 && g_comment[n-1] == '\n') {
+	  g_comment[n-1] = '\0';
+	}
+
+	n = strlen(g_comment);
+	if (n >= 1 && g_comment[n-1] == '\r') {
+	  g_comment[n-1] = '\0';
+	}
+
+	strcat (g_comment, "\"");
+
+	return;
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	aprs_ultimeter
+ *
+ * Purpose:	Decode Peet Brothers ULTIMETER Weather Station Info.
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	g_comment
+ *
+ * Description:	http://www.peetbros.com/shop/custom.aspx?recid=7 
+ *
+ * 		There are two different data formats in use.
+ *		One begins with $ULTW and is called "Packet Mode."  Example:
+ *
+ *		$ULTW009400DC00E21B8027730008890200010309001E02100000004C<CR><LF>
+ *
+ *		The other begins with !! and is called "logging mode."  Example:
+ *
+ *		!!000000A600B50000----------------001C01D500000017<CR><LF>
+ *
+ *
+ * Bugs:	Implementation is incomplete.
+ *		The example shown in the APRS protocol spec has a couple "----"
+ *		fields in the $ULTW message.  This should be rewritten to handle
+ *		each field separately to deal with missing pieces.
+ *
+ *------------------------------------------------------------------*/
+
+static void aprs_ultimeter (char *info, int ilen) 
+{
+
+				// Header = $ULTW 
+				// Data Fields 
+	short h_windpeak;	// 1. Wind Speed Peak over last 5 min. (0.1 kph) 
+	short h_wdir;		// 2. Wind Direction of Wind Speed Peak (0-255) 
+	short h_otemp;		// 3. Current Outdoor Temp (0.1 deg F) 
+	short h_totrain;	// 4. Rain Long Term Total (0.01 in.) 
+	short h_baro;		// 5. Current Barometer (0.1 mbar) 
+	short h_barodelta;	// 6. Barometer Delta Value(0.1 mbar) 
+	short h_barocorrl;	// 7. Barometer Corr. Factor(LSW) 
+	short h_barocorrm;	// 8. Barometer Corr. Factor(MSW) 
+	short h_ohumid;		// 9. Current Outdoor Humidity (0.1%) 
+	short h_date;		// 10. Date (day of year) 
+	short h_time;		// 11. Time (minute of day) 
+	short h_raintoday;	// 12. Today's Rain Total (0.01 inches)* 
+	short h_windave;	// 13. 5 Minute Wind Speed Average (0.1kph)* 
+				// Carriage Return & Line Feed
+				// *Some instruments may not include field 13, some may 
+				// not include 12 or 13. 
+				// Total size: 44, 48 or 52 characters (hex digits) + 
+				// header, carriage return and line feed. 
+
+	int n;
+
+	strcpy (g_msg_type, "Ultimeter");
+
+	if (*info == '$')
+ 	{
+	  n = sscanf (info+5, "%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx",
+			&h_windpeak,		
+			&h_wdir,		
+			&h_otemp,
+			&h_totrain,		
+			&h_baro,		
+			&h_barodelta,	
+			&h_barocorrl,	
+			&h_barocorrm,	
+			&h_ohumid,		 
+			&h_date,		
+			&h_time,		
+			&h_raintoday,	 	// not on some models.
+			&h_windave);		// not on some models.
+
+	  if (n >= 11 && n <= 13) {
+
+	    float windpeak, wdir, otemp, baro, ohumid;
+
+	    windpeak = KM_TO_MILES(h_windpeak * 0.1);
+	    wdir = (h_wdir & 0xff) * 360. / 256.;
+	    otemp = h_otemp * 0.1;
+	    baro = MBAR_TO_INHG(h_baro * 0.1);
+	    ohumid = h_ohumid * 0.1;
+	  
+	    sprintf (g_comment, "wind %.1f mph, direction %.0f, temperature %.1f, barometer %.2f, humidity %.0f",
+			windpeak, wdir, otemp, baro, ohumid);
+	  }
+	}
+
+	
+		// Header = !! 
+		// Data Fields 
+		// 1. Wind Speed (0.1 kph) 
+		// 2. Wind Direction (0-255) 
+		// 3. Outdoor Temp (0.1 deg F) 
+		// 4. Rain* Long Term Total (0.01 inches)  
+		// 5. Barometer (0.1 mbar) 	[ can be ---- ]
+		// 6. Indoor Temp (0.1 deg F) 	[ can be ---- ]
+		// 7. Outdoor Humidity (0.1%) 	[ can be ---- ]
+		// 8. Indoor Humidity (0.1%) 	[ can be ---- ]
+		// 9. Date (day of year) 
+		// 10. Time (minute of day) 
+		// 11. Today's Rain Total (0.01 inches)* 
+		// 12. 1 Minute Wind Speed Average (0.1kph)* 
+		// Carriage Return & Line Feed 
+		//
+		// *Some instruments may not include field 12, some may not include 11 or 12. 
+		// Total size: 40, 44 or 48 characters (hex digits) + header, carriage return and line feed
+
+	if (*info == '!')
+ 	{
+	  n = sscanf (info+2, "%4hx%4hx%4hx%4hx",
+			&h_windpeak,		
+			&h_wdir,		
+			&h_otemp,
+			&h_totrain);
+
+	  if (n == 4) {
+
+	    float windpeak, wdir, otemp;
+
+	    windpeak = KM_TO_MILES(h_windpeak * 0.1);
+	    wdir = (h_wdir & 0xff) * 360. / 256.;
+	    otemp = h_otemp * 0.1;
+	  
+	    sprintf (g_comment, "wind %.1f mph, direction %.0f, temperature %.1f\n",
+			windpeak, wdir, otemp);
+	  }
+
+	}
+
+} /* end aprs_ultimeter */
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	third_party_header
+ *
+ * Purpose:	Decode packet from a third party network.
+ *
+ * Inputs:	info 	- Pointer to Information field.
+ *		ilen 	- Information field length.
+ *
+ * Outputs:	g_comment
+ *
+ * Description:	
+ *
+ *------------------------------------------------------------------*/
+
+static void third_party_header (char *info, int ilen) 
+{
+	int n;
+
+	strcpy (g_msg_type, "Third Party Header");
+
+	/* more later? */
+
+} /* end third_party_header */
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	decode_position
+ *
+ * Purpose:	Decode the position & symbol information common to many message formats.
+ *
+ * Inputs:	ppos 	- Pointer to position & symbol fields.
+ *
+ * Returns:	g_lat
+ *		g_lon
+ *		g_symbol_table
+ *		g_symbol_code
+ *
+ * Description:	This provides resolution of about 60 feet.
+ *		This can be improved by using !DAO! in the comment.
+ *
+ *------------------------------------------------------------------*/
+
+
+static void decode_position (position_t *ppos)
+{
+
+	  g_lat = get_latitude_8 (ppos->lat);
+	  g_lon = get_longitude_9 (ppos->lon);
+
+	  g_symbol_table = ppos->sym_table_id;
+	  g_symbol_code = ppos->symbol_code;
+}
+
+/*------------------------------------------------------------------
+ *
+ * Function:	decode_compressed_position
+ *
+ * Purpose:	Decode the compressed position & symbol information common to many message formats.
+ *
+ * Inputs:	ppos 	- Pointer to compressed position & symbol fields.
+ *
+ * Returns:	g_lat
+ *		g_lon
+ *		g_symbol_table
+ *		g_symbol_code
+ *
+ *		One of the following:
+ *			g_course & g_speeed
+ *			g_altitude
+ *			g_range
+ *
+ * Description:	The compressed position provides resolution of around ???
+ *		This also includes course/speed or altitude.
+ *
+ *		It contains 13 bytes of the format:
+ *
+ *			symbol table	/, \, or overlay A-Z, a-j is mapped into 0-9
+ *
+ *			yyyy		Latitude, base 91.
+ * 
+ *			xxxx		Longitude, base 91.
+ *
+ *			symbol code
+ *
+ *			cs		Course/Speed or altitude.
+ *
+ *			t		Various "type" info.
+ *
+ *------------------------------------------------------------------*/
+
+
+static void decode_compressed_position (compressed_position_t *pcpos)
+{
+	if (pcpos->y[0] >= '!' && pcpos->y[0] <= '{' &&
+	    pcpos->y[1] >= '!' && pcpos->y[1] <= '{' &&
+	    pcpos->y[2] >= '!' && pcpos->y[2] <= '{' &&
+	    pcpos->y[3] >= '!' && pcpos->y[3] <= '{' ) 
+	{
+	  g_lat = 90 - ((pcpos->y[0]-33)*91*91*91 + (pcpos->y[1]-33)*91*91 + (pcpos->y[2]-33)*91 + (pcpos->y[3]-33)) / 380926.0;
+	}
+	else
+ 	{
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in compressed latitude.  Must be in range of '!' to '{'.\n");
+	  g_lat = G_UNKNOWN;
+	}
+	  
+	if (pcpos->x[0] >= '!' && pcpos->x[0] <= '{' &&
+	    pcpos->x[1] >= '!' && pcpos->x[1] <= '{' &&
+	    pcpos->x[2] >= '!' && pcpos->x[2] <= '{' &&
+	    pcpos->x[3] >= '!' && pcpos->x[3] <= '{' ) 
+	{
+	  g_lon = -180 + ((pcpos->x[0]-33)*91*91*91 + (pcpos->x[1]-33)*91*91 + (pcpos->x[2]-33)*91 + (pcpos->x[3]-33)) / 190463.0;
+	}
+	else 
+	{
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in compressed longitude.  Must be in range of '!' to '{'.\n");
+	  g_lon = G_UNKNOWN;
+	}
+
+	if (pcpos->sym_table_id == '/' || pcpos->sym_table_id == '\\' || isupper((int)(pcpos->sym_table_id))) {
+	  /* primary or alternate or alternate with upper case overlay. */
+	  g_symbol_table = pcpos->sym_table_id;
+   	}
+	else if (pcpos->sym_table_id >= 'a' && pcpos->sym_table_id <= 'j') {
+	  /* Lower case a-j are used to represent overlay characters 0-9 */
+	  /* because a digit here would mean normal (non-compressed) location. */
+	  g_symbol_table = pcpos->sym_table_id - 'a' + '0';
+	}
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid symbol table id for compressed position.\n");
+	  g_symbol_table = '/';
+	}
+
+	g_symbol_code = pcpos->symbol_code;
+
+	if (pcpos->c == ' ') {
+	  ; /* ignore other two bytes */
+	}
+	else if (((pcpos->t - 33) & 0x18) == 0x10) {
+	  g_altitude = pow(1.002, (pcpos->c - 33) * 91 + pcpos->s - 33);
+	}
+	else if (pcpos->c == '{')
+	{
+	  g_range = 2.0 * pow(1.08, pcpos->s - 33);
+	}
+	else if (pcpos->c >= '!' && pcpos->c <= 'z')
+	{
+	  /* For a weather station, this is wind information. */
+	  g_course = (pcpos->c - 33) * 4;
+	  g_speed = KNOTS_TO_MPH(pow(1.08, pcpos->s - 33) - 1.0);
+	}
+
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	get_latitude_8
+ *
+ * Purpose:	Convert 8 byte latitude encoding to degrees.
+ *
+ * Inputs:	plat 	- Pointer to first byte.
+ *
+ * Returns:	Double precision value in degrees.  Negative for South.
+ *
+ * Description:	Latitude is expressed as a fixed 8-character field, in degrees 
+ *		and decimal minutes (to two decimal places), followed by the 
+ *		letter N for north or S for south.
+ *		The protocol spec specifies upper case but I've seen lower
+ *		case so this will accept either one.
+ *		Latitude degrees are in the range 00 to 90. Latitude minutes 
+ *		are expressed as whole minutes and hundredths of a minute, 
+ *		separated by a decimal point.
+ *		For example:
+ *		4903.50N is 49 degrees 3 minutes 30 seconds north.
+ *		In generic format examples, the latitude is shown as the 8-character 
+ *		string ddmm.hhN (i.e. degrees, minutes and hundredths of a minute north).
+ *
+ * Bug:		We don't properly deal with position ambiguity where trailing
+ *		digits might be replaced by spaces.  We simply treat them like zeros.	
+ *
+ * Errors:	Return G_UNKNOWN for any type of error.
+ *
+ *		Should probably print an error message.
+ *
+ *------------------------------------------------------------------*/
+
+double get_latitude_8 (char *p)
+{
+	struct lat_s {
+	  unsigned char deg[2];
+	  unsigned char minn[2];
+	  char dot;
+	  unsigned char hmin[2];
+	  char ns;
+	} *plat;
+
+	double result = 0;
+	
+	plat = (void *)p;
+
+	if (isdigit(plat->deg[0]))
+	  result += ((plat->deg[0]) - '0') * 10;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in latitude.  Expected 0-9 for tens of degrees.\n");
+	  return (G_UNKNOWN);
+	}
+
+	if (isdigit(plat->deg[1]))
+	  result += ((plat->deg[1]) - '0') * 1;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in latitude.  Expected 0-9 for degrees.\n");
+	  return (G_UNKNOWN);
+	}
+
+	if (plat->minn[0] >= '0' || plat->minn[0] <= '5')
+	  result += ((plat->minn[0]) - '0') * (10. / 60.);
+	else if (plat->minn[0] == ' ')
+	  ;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in latitude.  Expected 0-5 for tens of minutes.\n");
+	  return (G_UNKNOWN);
+	}
+
+	if (isdigit(plat->minn[1]))
+	  result += ((plat->minn[1]) - '0') * (1. / 60.);
+	else if (plat->minn[1] == ' ')
+	  ;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in latitude.  Expected 0-9 for minutes.\n");
+	  return (G_UNKNOWN);
+	}
+
+	if (plat->dot != '.') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Unexpected character \"%c\" found where period expected in latitude.\n", plat->dot);
+	  return (G_UNKNOWN);
+	} 
+
+	if (isdigit(plat->hmin[0]))
+	  result += ((plat->hmin[0]) - '0') * (0.1 / 60.);
+	else if (plat->hmin[0] == ' ')
+	  ;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in latitude.  Expected 0-9 for tenths of minutes.\n");
+	  return (G_UNKNOWN);
+	}
+
+	if (isdigit(plat->hmin[1]))
+	  result += ((plat->hmin[1]) - '0') * (0.01 / 60.);
+	else if (plat->hmin[1] == ' ')
+	  ;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in latitude.  Expected 0-9 for hundredths of minutes.\n");
+	  return (G_UNKNOWN);
+	}
+
+// The spec requires upper case for hemisphere.  Accept lower case but warn.
+
+	if (plat->ns == 'N') {
+	  return (result);
+        }
+        else if (plat->ns == 'n') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Warning: Lower case n found for latitude hemisphere.  Specification requires upper case N or S.\n");	  
+	  return (result);
+	}
+	else if (plat->ns == 'S') {
+	  return ( - result);
+	}
+	else if (plat->ns == 's') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Warning: Lower case s found for latitude hemisphere.  Specification requires upper case N or S.\n");	  
+	  return ( - result);
+	}
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Error: '%c' found for latitude hemisphere.  Specification requires upper case N or s.\n", plat->ns);	  
+	  return (G_UNKNOWN);	
+	}	
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	get_longitude_9
+ *
+ * Purpose:	Convert 9 byte longitude encoding to degrees.
+ *
+ * Inputs:	plat 	- Pointer to first byte.
+ *
+ * Returns:	Double precision value in degrees.  Negative for West.
+ *
+ * Description:	Longitude is expressed as a fixed 9-character field, in degrees and 
+ *		decimal minutes (to two decimal places), followed by the letter E 
+ *		for east or W for west.
+ *		Longitude degrees are in the range 000 to 180. Longitude minutes are
+ *		expressed as whole minutes and hundredths of a minute, separated by a
+ *		decimal point.
+ *		For example:
+ *		07201.75W is 72 degrees 1 minute 45 seconds west.
+ *		In generic format examples, the longitude is shown as the 9-character 
+ *		string dddmm.hhW (i.e. degrees, minutes and hundredths of a minute west).
+ *
+ * Bug:		We don't properly deal with position ambiguity where trailing
+ *		digits might be replaced by spaces.  We simply treat them like zeros.	
+ *
+ * Errors:	Return G_UNKNOWN for any type of error.
+ *
+ * Example:	
+ *
+ *------------------------------------------------------------------*/
+
+
+double get_longitude_9 (char *p)
+{
+	struct lat_s {
+	  unsigned char deg[3];
+	  unsigned char minn[2];
+	  char dot;
+	  unsigned char hmin[2];
+	  char ew;
+	} *plon;
+
+	double result = 0;
+	
+	plon = (void *)p;
+
+	if (plon->deg[0] == '0' || plon->deg[0] == '1')
+	  result += ((plon->deg[0]) - '0') * 100;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in longitude.  Expected 0 or 1 for hundreds of degrees.\n");
+	  return (G_UNKNOWN);
+	}
+
+	if (isdigit(plon->deg[1]))
+	  result += ((plon->deg[1]) - '0') * 10;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in longitude.  Expected 0-9 for tens of degrees.\n");
+	  return (G_UNKNOWN);
+	}
+
+	if (isdigit(plon->deg[2]))
+	  result += ((plon->deg[2]) - '0') * 1;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in longitude.  Expected 0-9 for degrees.\n");
+	  return (G_UNKNOWN);
+	}
+
+	if (plon->minn[0] >= '0' || plon->minn[0] <= '5')
+	  result += ((plon->minn[0]) - '0') * (10. / 60.);
+	else if (plon->minn[0] == ' ')
+	  ;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in longitude.  Expected 0-5 for tens of minutes.\n");
+	  return (G_UNKNOWN);
+	}
+
+	if (isdigit(plon->minn[1]))
+	  result += ((plon->minn[1]) - '0') * (1. / 60.);
+	else if (plon->minn[1] == ' ')
+	  ;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in longitude.  Expected 0-9 for minutes.\n");
+	  return (G_UNKNOWN);
+	}
+
+	if (plon->dot != '.') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Unexpected character \"%c\" found where period expected in longitude.\n", plon->dot);
+	  return (G_UNKNOWN);
+	} 
+
+	if (isdigit(plon->hmin[0]))
+	  result += ((plon->hmin[0]) - '0') * (0.1 / 60.);
+	else if (plon->hmin[0] == ' ')
+	  ;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in longitude.  Expected 0-9 for tenths of minutes.\n");
+	  return (G_UNKNOWN);
+	}
+
+	if (isdigit(plon->hmin[1]))
+	  result += ((plon->hmin[1]) - '0') * (0.01 / 60.);
+	else if (plon->hmin[1] == ' ')
+	  ;
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Invalid character in longitude.  Expected 0-9 for hundredths of minutes.\n");
+	  return (G_UNKNOWN);
+	}
+
+// The spec requires upper case for hemisphere.  Accept lower case but warn.
+
+	if (plon->ew == 'E') {
+	  return (result);
+        }
+        else if (plon->ew == 'e') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Warning: Lower case e found for longitude hemisphere.  Specification requires upper case E or W.\n");	  
+	  return (result);
+	}
+	else if (plon->ew == 'W') {
+	  return ( - result);
+	}
+	else if (plon->ew == 'w') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Warning: Lower case w found for longitude hemisphere.  Specification requires upper case E or W.\n");	  
+	  return ( - result);
+	}
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Error: '%c' found for longitude hemisphere.  Specification requires upper case E or W.\n", plon->ew);	  
+	  return (G_UNKNOWN);	
+	}		
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	get_timestamp
+ *
+ * Purpose:	Convert 7 byte timestamp to unix time value.
+ *
+ * Inputs:	p 	- Pointer to first byte.
+ *
+ * Returns:	time_t data type. (UTC)
+ *
+ * Description:	
+ *
+ *		Day/Hours/Minutes (DHM) format is a fixed 7-character field, consisting of
+ *		a 6-digit day/time group followed by a single time indicator character (z or
+ *		/). The day/time group consists of a two-digit day-of-the-month (01�31) and
+ *		a four-digit time in hours and minutes.
+ *		Times can be expressed in zulu (UTC/GMT) or local time. For example:
+ *
+ *		  092345z is 2345 hours zulu time on the 9th day of the month.
+ *		  092345/ is 2345 hours local time on the 9th day of the month.
+ *
+ *		It is recommended that future APRS implementations only transmit zulu
+ *		format on the air.
+ *
+ *		Note: The time in Status Reports may only be in zulu format.
+ *
+ *		Hours/Minutes/Seconds (HMS) format is a fixed 7-character field,
+ *		consisting of a 6-digit time in hours, minutes and seconds, followed by the h
+ *		time-indicator character. For example:
+ *
+ *		  234517h is 23 hours 45 minutes and 17 seconds zulu.
+ *
+ *		Note: This format may not be used in Status Reports.
+ *
+ *		Month/Day/Hours/Minutes (MDHM) format is a fixed 8-character field,
+ *		consisting of the month (01�12) and day-of-the-month (01�31), followed by
+ *		the time in hours and minutes zulu. For example:
+ *
+ *		  10092345 is 23 hours 45 minutes zulu on October 9th.
+ *
+ *		This format is only used in reports from stand-alone �positionless� weather
+ *		stations (i.e. reports that do not contain station position information).
+ *
+ *
+ * Bugs:	Local time not implemented yet.
+ *		8 character form not implemented yet.
+ *
+ *		Boundary conditions are not handled properly.
+ *		For example, suppose it is 00:00:03 on January 1.
+ *		We receive a timestamp of 23:59:58 (which was December 31).
+ *		If we simply replace the time, and leave the current date alone,
+ *		the result is about a day into the future.
+ *
+ *
+ * Example:	
+ *
+ *------------------------------------------------------------------*/
+
+
+time_t get_timestamp (char *p)
+{
+	struct dhm_s {
+	  char day[2];
+	  char hours[2];
+	  char minutes[2];
+	  char tic;		/* Time indicator character. */
+				/* z = UTC. */
+				/* / = local - not implemented yet. */
+	} *pdhm;
+
+	struct hms_s {
+	  char hours[2];
+	  char minutes[2];
+	  char seconds[2];
+	  char tic;		/* Time indicator character. */
+				/* h = UTC. */
+	} *phms;
+
+	struct tm *ptm;
+
+	time_t ts;
+
+	ts = time(NULL);
+	ptm = gmtime(&ts);
+
+	pdhm = (void *)p;
+	phms = (void *)p;
+
+	if (pdhm->tic == 'z' || pdhm->tic == '/')   /* Wrong! */
+	{
+	  int j;
+
+	  j = (pdhm->day[0] - '0') * 10 + pdhm->day[1] - '0';
+	  //text_color_set(DW_COLOR_DECODED);
+	  //dw_printf("Changing day from %d to %d\n", ptm->tm_mday, j);
+	  ptm->tm_mday = j;
+
+	  j = (pdhm->hours[0] - '0') * 10 + pdhm->hours[1] - '0';
+	  //dw_printf("Changing hours from %d to %d\n", ptm->tm_hour, j);
+	  ptm->tm_hour = j;
+
+	  j = (pdhm->minutes[0] - '0') * 10 + pdhm->minutes[1] - '0';
+	  //dw_printf("Changing minutes from %d to %d\n", ptm->tm_min, j);
+	  ptm->tm_min = j;
+
+	} 
+	else if (phms->tic == 'h') 
+	{
+	  int j;
+
+	  j = (phms->hours[0] - '0') * 10 + phms->hours[1] - '0';
+	  //text_color_set(DW_COLOR_DECODED);
+	  //dw_printf("Changing hours from %d to %d\n", ptm->tm_hour, j);
+	  ptm->tm_hour = j;
+
+	  j = (phms->minutes[0] - '0') * 10 + phms->minutes[1] - '0';
+	  //dw_printf("Changing minutes from %d to %d\n", ptm->tm_min, j);
+	  ptm->tm_min = j;
+
+	  j = (phms->seconds[0] - '0') * 10 + phms->seconds[1] - '0';
+	  //dw_printf("%sChanging seconds from %d to %d\n", ptm->tm_sec, j);
+	  ptm->tm_sec = j;
+	} 
+	
+	return (mktime(ptm));
+}
+
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	get_maidenhead
+ *
+ * Purpose:	See if we have a maidenhead locator.
+ *
+ * Inputs:	p 	- Pointer to first byte.
+ *
+ * Returns:	0 = not found.
+ *		4 = possible 4 character locator found.
+ *		6 = possible 6 character locator found.
+ *
+ *		It is not stored anywhere or processed.
+ *
+ * Description:	
+ *
+ *		The maidenhead locator system is sometimes used as a more compact, 
+ *		and less precise, alternative to numeric latitude and longitude.
+ *
+ *		It is composed of:
+ *			a pair of letters in range A to R.
+ *			a pair of digits in range of 0 to 9.
+ *			a pair of letters in range of A to X.
+ *
+ * 		The APRS spec says that all letters must be transmitted in upper case.
+ *
+ *
+ * Examples from APRS spec:	
+ *
+ *		IO91SX
+ *		IO91
+ *
+ *
+ *------------------------------------------------------------------*/
+
+
+int get_maidenhead (char *p)
+{
+
+	if (toupper(p[0]) >= 'A' && toupper(p[0]) <= 'R' &&
+	    toupper(p[1]) >= 'A' && toupper(p[1]) <= 'R' &&
+	    isdigit(p[2]) && isdigit(p[3])) {
+
+	  /* We have 4 characters matching the rule. */
+
+	  if (islower(p[0]) || islower(p[1])) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("Warning: Lower case letter in Maidenhead locator.  Specification requires upper case.\n");	  
+	  }
+
+	  if (toupper(p[4]) >= 'A' && toupper(p[4]) <= 'X' &&
+	      toupper(p[5]) >= 'A' && toupper(p[5]) <= 'X') {
+
+	    /* We have 6 characters matching the rule. */
+
+	    if (islower(p[4]) || islower(p[5])) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf("Warning: Lower case letter in Maidenhead locator.  Specification requires upper case.\n");	  
+	    }
+	  
+	    return 6;
+	  }
+	
+	  return 4;
+	}
+
+	return 0;
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	get_latitude_nmea
+ *
+ * Purpose:	Convert NMEA latitude encoding to degrees.
+ *
+ * Inputs:	pstr 	- Pointer to numeric string.
+ *		phemi	- Pointer to following field.  Should be N or S.
+ *
+ * Returns:	Double precision value in degrees.  Negative for South.
+ *
+ * Description:	Latitude field has
+ *			2 digits for degrees
+ *			2 digits for minutes
+ *			period
+ *			Variable number of fractional digits for minutes.
+ *			I've seen 2, 3, and 4 fractional digits.
+ *
+ *
+ * Bugs:	Very little validation of data.
+ *
+ * Errors:	Return constant G_UNKNOWN for any type of error.
+ *		Could we use special "NaN" code?
+ *
+ *------------------------------------------------------------------*/
+
+
+static double get_latitude_nmea (char *pstr, char *phemi)
+{
+
+	double lat;
+
+	if ( ! isdigit((unsigned char)(pstr[0]))) return (G_UNKNOWN);
+
+	if (pstr[4] != '.') return (G_UNKNOWN);
+
+
+	lat = (pstr[0] - '0') * 10 + (pstr[1] - '0') + atof(pstr+2) / 60.0;
+
+	if (lat < 0 || lat > 90) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Error: Latitude not in range of 0 to 90.\n");	  
+	}
+
+	// Saw this one time:
+	//	$GPRMC,000000,V,0000.0000,0,00000.0000,0,000,000,000000,,*01
+
+	// If location is unknown, I think the hemisphere should be
+	// an empty string.  TODO: Check on this.
+
+	if (*phemi != 'N' && *phemi != 'S' && *phemi != '\0') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Error: Latitude hemisphere should be N or S.\n");	  
+	}
+
+	if (*phemi == 'S') lat = ( - lat);
+
+	return (lat);
+}
+
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	get_longitude_nmea
+ *
+ * Purpose:	Convert NMEA longitude encoding to degrees.
+ *
+ * Inputs:	pstr 	- Pointer to numeric string.
+ *		phemi	- Pointer to following field.  Should be E or W.
+ *
+ * Returns:	Double precision value in degrees.  Negative for West.
+ *
+ * Description:	Longitude field has
+ *			3 digits for degrees
+ *			2 digits for minutes
+ *			period
+ *			Variable number of fractional digits for minutes
+ *
+ *
+ * Bugs:	Very little validation of data.
+ *
+ * Errors:	Return constant G_UNKNOWN for any type of error.
+ *		Could we use special "NaN" code?
+ *
+ *------------------------------------------------------------------*/
+
+
+static double get_longitude_nmea (char *pstr, char *phemi)
+{
+	double lon;
+
+	if ( ! isdigit((unsigned char)(pstr[0]))) return (G_UNKNOWN);
+
+	if (pstr[5] != '.') return (G_UNKNOWN);
+
+	lon = (pstr[0] - '0') * 100 + (pstr[1] - '0') * 10 + (pstr[2] - '0') + atof(pstr+3) / 60.0;
+
+	if (lon < 0 || lon > 180) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Error: Longitude not in range of 0 to 180.\n");	  
+	}
+	
+	if (*phemi != 'E' && *phemi != 'W' && *phemi != '\0') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf("Error: Longitude hemisphere should be E or W.\n");	  
+	}
+
+	if (*phemi == 'W') lon = ( - lon);
+
+	return (lon);
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	data_extension_comment
+ *
+ * Purpose:	A fixed length 7-byte field may follow APRS position data.
+ *
+ * Inputs:	pdext	- Pointer to optional data extension and comment.
+ *
+ * Returns:	true if a data extension was found.
+ *
+ * Outputs:	One or more of the following, depending the data found:
+ *	
+ *			g_course
+ *			g_speed
+ *			g_power 
+ *			g_height 
+ *			g_gain 
+ *			g_directivity 
+ *			g_range
+ *
+ *		Anything left over will be put in 
+ *
+ *			g_comment			
+ *
+ * Description:	
+ *
+ *
+ *
+ *------------------------------------------------------------------*/
+
+const char *dir[9] = { "omni", "NE", "E", "SE", "S", "SW", "W", "NW", "N" };
+
+static int data_extension_comment (char *pdext)
+{
+	int n;
+
+	if (strlen(pdext) < 7) {
+	  strcpy (g_comment, pdext);
+	  return 0;
+	}
+
+/* Tyy/Cxx - Area object descriptor. */
+
+	if (pdext[0] == 'T' &&
+		pdext[3] == '/' &&
+	 	pdext[4] == 'C')
+	{
+	  /* not decoded at this time */
+	  process_comment (pdext+7, -1);
+	  return 1;
+	}
+
+/* CSE/SPD */
+/* For a weather station (symbol code _) this is wind. */
+/* For others, it would be course and speed. */
+
+	if (pdext[3] == '/')
+	{
+	  if (sscanf (pdext, "%3d", &n))
+	  {
+	    g_course = n;
+	  }
+	  if (sscanf (pdext+4, "%3d", &n))
+	  {
+	    g_speed = KNOTS_TO_MPH(n);
+	  }
+
+	  /* Bearing and Number/Range/Quality? */
+
+	  if (pdext[7] == '/' && pdext[11] == '/') 
+	  {
+	    process_comment (pdext + 7 + 8, -1);
+	  }
+	  else {
+	    process_comment (pdext+7, -1);
+	  }
+	  return 1;
+	}
+
+/* check for Station power, height, gain. */
+
+	if (strncmp(pdext, "PHG", 3) == 0)
+	{
+	  g_power = (pdext[3] - '0') * (pdext[3] - '0');
+	  g_height = (1 << (pdext[4] - '0')) * 10;
+	  g_gain = pdext[5] - '0';
+	  if (pdext[6] >= '0' && pdext[6] <= '8') {
+	    strcpy (g_directivity, dir[pdext[6]-'0']);
+	  }
+
+	  process_comment (pdext+7, -1);
+	  return 1;
+	}
+
+/* check for precalculated radio range. */
+
+	if (strncmp(pdext, "RNG", 3) == 0)
+	{
+	  if (sscanf (pdext+3, "%4d", &n))
+	  {
+	    g_range = n;
+	  }
+	  process_comment (pdext+7, -1);
+	  return 1;
+	}
+
+/* DF signal strength,  */
+
+	if (strncmp(pdext, "DFS", 3) == 0)
+	{
+	  //g_strength = pdext[3] - '0';
+	  g_height = (1 << (pdext[4] - '0')) * 10;
+	  g_gain = pdext[5] - '0';
+	  if (pdext[6] >= '0' && pdext[6] <= '8') {
+	    strcpy (g_directivity, dir[pdext[6]-'0']);
+	  }
+
+	  process_comment (pdext+7, -1);
+	  return 1;
+	}
+
+	process_comment (pdext, -1);
+	return 0;
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	decode_tocall
+ *
+ * Purpose:	Extract application from the destination.
+ *
+ * Inputs:	dest	- Destination address.
+ *			Don't care if SSID is present or not.
+ *
+ * Outputs:	g_mfr
+ *
+ * Description:	For maximum flexibility, we will read the
+ *		data file at run time rather than compiling it in.
+ *
+ *		For the most recent version, download from:
+ *
+ *		http://www.aprs.org/aprs11/tocalls.txt
+ *
+ *		Windows version:  File must be in current working directory.
+ *
+ *		Linux version: Search order is current working directory
+ *			then /usr/share/direwolf directory.
+ *
+ *------------------------------------------------------------------*/
+
+#define MAX_TOCALLS 150
+
+static struct tocalls_s {
+	unsigned char len;
+	char prefix[7];
+	char *description;
+} tocalls[MAX_TOCALLS];
+
+static int num_tocalls = 0;
+
+static int tocall_cmp (const struct tocalls_s *x, const struct tocalls_s *y) 
+{
+	if (x->len != y->len) return (y->len - x->len);
+	return (strcmp(x->prefix, y->prefix));
+}
+
+static void decode_tocall (char *dest)
+{
+	FILE *fp;
+	int n;
+	static int first_time = 1;
+	char stuff[100];
+	char *p;
+	char *r;
+
+	//dw_printf("debug: decode_tocall(\"%s\")\n", dest);
+
+/*
+ * Extract the calls and descriptions from the file.
+ *
+ * Use only lines with exactly these formats:
+ *
+ *       APN          Network nodes, digis, etc
+ *	      APWWxx  APRSISCE win32 version
+ *	|     |       |
+ *	00000000001111111111      	
+ *	01234567890123456789...
+ *
+ * Matching will be with only leading upper case and digits.
+ */
+
+// TODO:  Look for this in multiple locations.
+// For example, if application was installed in /usr/local/bin,
+// we might want to put this in /usr/local/share/aprs
+
+// If search strategy changes, be sure to keep symbols_init in sync.
+
+	if (first_time) {
+
+	  fp = fopen("tocalls.txt", "r");
+#ifndef __WIN32__
+	  if (fp == NULL) {
+	    fp = fopen("/usr/share/direwolf/tocalls.txt", "r");
+	  }
+#endif
+	  if (fp != NULL) {
+
+	    while (fgets(stuff, sizeof(stuff), fp) != NULL && num_tocalls < MAX_TOCALLS) {
+	      
+	      p = stuff + strlen(stuff) - 1;
+	      while (p >= stuff && (*p == '\r' || *p == '\n')) {
+	        *p-- = '\0';
+	      }
+
+	      // printf("debug: %s\n", stuff);
+
+	      if (stuff[0] == ' ' && 
+		  stuff[4] == ' ' &&
+		  stuff[5] == ' ' &&
+		  stuff[6] == 'A' && 
+		  stuff[7] == 'P' && 
+		  stuff[12] == ' ' &&
+		  stuff[13] == ' ' ) {
+
+	        p = stuff + 6;
+	        r = tocalls[num_tocalls].prefix;
+	        while (isupper((int)(*p)) || isdigit((int)(*p))) {
+	          *r++ = *p++;
+	        }
+	        *r = '\0';
+	        if (strlen(tocalls[num_tocalls].prefix) > 2) {
+	          tocalls[num_tocalls].description = strdup(stuff+14);
+		  tocalls[num_tocalls].len = strlen(tocalls[num_tocalls].prefix);
+	          // dw_printf("debug: %d '%s' -> '%s'\n", tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description);
+
+	          num_tocalls++;
+	        }
+	      }
+	      else if (stuff[0] == ' ' && 
+		  stuff[1] == 'A' && 
+		  stuff[2] == 'P' && 
+		  isupper((int)(stuff[3])) &&
+		  stuff[4] == ' ' &&
+		  stuff[5] == ' ' &&
+		  stuff[6] == ' ' &&
+		  stuff[12] == ' ' &&
+		  stuff[13] == ' ' ) {
+
+	        p = stuff + 1;
+	        r = tocalls[num_tocalls].prefix;
+	        while (isupper((int)(*p)) || isdigit((int)(*p))) {
+	          *r++ = *p++;
+	        }
+	        *r = '\0';
+	        if (strlen(tocalls[num_tocalls].prefix) > 2) {
+	          tocalls[num_tocalls].description = strdup(stuff+14);
+		  tocalls[num_tocalls].len = strlen(tocalls[num_tocalls].prefix);
+	          // dw_printf("debug: %d '%s' -> '%s'\n", tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description);
+
+	          num_tocalls++;
+	        }
+	      }
+	    }
+	    fclose(fp);
+
+/*
+ * Sort by decreasing length so the search will go
+ * from most specific to least specific.
+ * Example:  APY350 or APY008 would match those specific
+ * models before getting to the more generic APY.
+ */
+
+#if __WIN32__
+	    qsort (tocalls, num_tocalls, sizeof(struct tocalls_s), tocall_cmp);
+#else
+	    qsort (tocalls, num_tocalls, sizeof(struct tocalls_s), (__compar_fn_t)tocall_cmp);
+#endif
+	  }
+	  else {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("Warning: Could not open 'tocalls.txt'.\n");
+	    dw_printf("System types in the destination field will not be decoded.\n");
+	  }
+
+	
+	  first_time = 0;
+	}
+
+
+	for (n=0; n<num_tocalls; n++) {
+	  if (strncmp(dest, tocalls[n].prefix, tocalls[n].len) == 0) {
+	    strncpy (g_mfr, tocalls[n].description, sizeof(g_mfr)-1);
+	    g_mfr[sizeof(g_mfr)-1] = '\0';
+	    return;
+	  }
+	}
+
+} /* end decode_tocall */ 
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	process_comment
+ *
+ * Purpose:	Extract optional items from the comment.
+ *
+ * Inputs:	pstart		- Pointer to start of left over information field.
+ *
+ *		clen		- Length of comment or -1 to take it all.
+ *
+ * Outputs:	g_comment
+ *
+ * Description:	After processing fixed and possible optional parts
+ *		of the message, everything left over is a comment.
+ *
+ *		Except!!!
+ *
+ *		There are could be some other pieces of data, with 
+ *		particular formats, buried in there.
+ *		Pull out those special items and put everything 
+ *		else into g_comment.
+ *
+ * References:	http://www.aprs.org/info/freqspec.txt
+ *
+ *			999.999MHz T100 +060	Voice frequency.
+ *		
+ *		http://www.aprs.org/datum.txt
+ *
+ *			!DAO!			APRS precision and Datum option.
+ *
+ *		Protocol reference, end of chaper 6.
+ *
+ *			/A=123456		Altitude
+ *
+ *
+ *------------------------------------------------------------------*/
+
+#define sign(x) (((x)>=0)?1:(-1))
+
+static void process_comment (char *pstart, int clen)
+{
+	static int first_time = 1;
+	static regex_t freq_re;	/* These must be static! */
+	static regex_t dao_re;	/* These must be static! */
+	static regex_t alt_re;	/* These must be static! */
+	int e;
+	char emsg[100];
+#define MAXMATCH 1
+	regmatch_t match[MAXMATCH];
+	char temp[256];
+
+/*
+ * No sense in recompiling the patterns and freeing every time.
+ */	
+	if (first_time) 
+	{
+/*
+ * Present, frequency must be at the at the beginning.
+ * Others can be anywhere in the comment.
+ */
+		/* incomplete */
+	  e = regcomp (&freq_re, "^[0-9A-O][0-9][0-9]\\.[0-9][0-9][0-9 ]MHz( [TCDtcd][0-9][0-9][0-9]| Toff)?( [+-][0-9][0-9][0-9])?", REG_EXTENDED);
+	  if (e) {
+	    regerror (e, &freq_re, emsg, sizeof(emsg));
+	    dw_printf("%s:%d: %s\n", __FILE__, __LINE__, emsg);
+	  }
+
+	  e = regcomp (&dao_re, "!([A-Z][0-9 ][0-9 ]|[a-z][!-} ][!-} ])!", REG_EXTENDED);
+	  if (e) {
+	    regerror (e, &dao_re, emsg, sizeof(emsg));
+	    dw_printf("%s:%d: %s\n", __FILE__, __LINE__, emsg);
+	  }
+
+	  e = regcomp (&alt_re, "/A=[0-9][0-9][0-9][0-9][0-9][0-9]", REG_EXTENDED);
+	  if (e) {
+	    regerror (e, &alt_re, emsg, sizeof(emsg));
+	    dw_printf("%s:%d: %s\n", __FILE__, __LINE__, emsg);
+	  }
+
+	  first_time = 0;
+	}
+
+	if (clen >= 0) {
+	  assert (clen < sizeof(g_comment));
+	  memcpy (g_comment, pstart, (size_t)clen);
+	  g_comment[clen] = '\0';
+	}
+	else {
+	  strcpy (g_comment, pstart);
+	}
+	//dw_printf("\nInitial comment='%s'\n", g_comment);
+
+
+/*
+ * Frequency.
+ * Just pull it out from comment. 
+ * No futher interpretation at this time.
+ */
+
+	if (regexec (&freq_re, g_comment, MAXMATCH, match, 0) == 0) 
+	{
+
+          //dw_printf("start=%d, end=%d\n", (int)(match[0].rm_so), (int)(match[0].rm_eo));
+
+	  strcpy (temp, g_comment + match[0].rm_eo);
+
+	  g_comment[match[0].rm_eo] = '\0';
+          strcpy (g_freq, g_comment + match[0].rm_so);
+
+	  strcpy (g_comment + match[0].rm_so, temp);
+	}
+
+/*
+ * Latitude and Longitude in the form DD MM.HH has a resolution of about 60 feet.
+ * The !DAO! option allows another digit or [almost two] for greater resolution.
+ */
+
+	if (regexec (&dao_re, g_comment, MAXMATCH, match, 0) == 0) 
+	{
+
+	  int d = g_comment[match[0].rm_so+1];
+	  int a = g_comment[match[0].rm_so+2];
+	  int o = g_comment[match[0].rm_so+3];
+
+          //dw_printf("start=%d, end=%d\n", (int)(match[0].rm_so), (int)(match[0].rm_eo));
+
+	  if (isupper(d)) 
+	  {
+/*
+ * This adds one extra digit to each.  Dao adds extra digit like:
+ *
+ *		Lat:	 DD MM.HHa
+ *		Lon:	DDD HH.HHo
+ */
+ 	    if (isdigit(a)) {
+	      g_lat += (a - '0') / 60000.0 * sign(g_lat);
+	    }
+ 	    if (isdigit(o)) {
+	      g_lon += (o - '0') / 60000.0 * sign(g_lon);
+	    }
+	  }
+	  else if (islower(d)) 
+	  {
+/*
+ * This adds almost two extra digits to each like this:
+ *
+ *		Lat:	 DD MM.HHxx
+ *		Lon:	DDD HH.HHxx
+ *
+ * The original character range '!' to '}' is first converted
+ * to an integer in range of 0 to 90.  It is multiplied by 1.1
+ * to stretch the numeric range to be 0 to 99.
+ */
+ 	    if (a >= '!' && a <= '}') {
+	      g_lat += (a - '!') * 1.1 / 600000.0 * sign(g_lat);
+	    }
+ 	    if (o >= '!' && o <= '}') {
+	      g_lon += (o - '!') * 1.1 / 600000.0 * sign(g_lon);
+	    }
+	  }
+
+	  strcpy (temp, g_comment + match[0].rm_eo);
+	  strcpy (g_comment + match[0].rm_so, temp);
+	}
+
+/*
+ * Altitude in feet.  /A=123456
+ */
+
+	if (regexec (&alt_re, g_comment, MAXMATCH, match, 0) == 0) 
+	{
+
+          //dw_printf("start=%d, end=%d\n", (int)(match[0].rm_so), (int)(match[0].rm_eo));
+
+	  strcpy (temp, g_comment + match[0].rm_eo);
+
+	  g_comment[match[0].rm_eo] = '\0';
+          g_altitude = atoi(g_comment + match[0].rm_so + 3);
+
+	  strcpy (g_comment + match[0].rm_so, temp);
+	}
+
+	//dw_printf("Final comment='%s'\n", g_comment);
+
+}
+
+/* end process_comment */
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	main
+ *
+ * Purpose:	Main program for standalone test program.
+ *
+ * Inputs:	stdin for raw data to decode.
+ *		This is in the usual display format either from
+ *		a TNC, findu.com, aprs.fi, etc.  e.g.
+ *
+ *		N1EDF-9>T2QT8Y,W1CLA-1,WIDE1*,WIDE2-2,00000:`bSbl!Mv/`"4%}_ <0x0d>
+ *
+ *		WB2OSZ-1>APN383,qAR,N1EDU-2:!4237.14NS07120.83W#PHG7130Chelmsford, MA
+ *
+ *
+ * Outputs:	stdout
+ *
+ * Description:	Compile like this to make a standalone test program.
+ *
+ *		gcc -o decode_aprs -DTEST decode_aprs.c ax25_pad.c	
+ *
+ *		./decode_aprs < decode_aprs.txt
+ *
+ *		aprs.fi precedes raw data with a time stamp which you
+ *		would need to remove first.
+ *
+ *		cut -c26-999 tmp/kj4etp-9.txt | decode_aprs.exe
+ *
+ *
+ * Restriction:	MIC-E message type can be problematic because it
+ *		it can use unprintable characters in the information field.
+ *
+ *		Dire Wolf and aprs.fi print it in hexadecimal.  Example:
+ *
+ *		KB1KTR-8>TR3U6T,KB1KTR-9*,WB2OSZ-1*,WIDE2*,qAR,W1XM:`c1<0x1f>l!t>/>"4^}
+ *		                                                       ^^^^^^
+ *		                                                       ||||||
+ *		What does findu.com do in this case?
+ *
+ *		ax25_from_text recognizes this representation so it can be used
+ *		to decode raw data later.
+ *
+ *
+ *------------------------------------------------------------------*/
+
+#if TEST
+
+
+int main (int argc, char *argv[]) 
+{
+	char stuff[300];
+	char *p;	
+	packet_t pp;
+
+#if __WIN32__
+
+// Select UTF-8 code page for console output.
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686036(v=vs.85).aspx
+// This is the default I see for windows terminal:  
+// >chcp
+// Active code page: 437
+
+	//Restore on exit? oldcp = GetConsoleOutputCP();
+	SetConsoleOutputCP(CP_UTF8);
+
+#else
+
+/*
+ * Default on Raspian & Ubuntu Linux is fine.  Don't know about others.
+ *
+ * Should we look at LANG environment variable and issue a warning
+ * if it doesn't look something like  en_US.UTF-8 ?
+ */
+
+#endif	
+	if (argc >= 2) {
+	  if (freopen (argv[1], "r", stdin) == NULL) {
+	    fprintf(stderr, "Can't open %s for read.\n", argv[1]);
+	    exit(1);
+	  }
+	}
+
+	text_color_init(1);
+	text_color_set(DW_COLOR_INFO);
+
+	while (fgets(stuff, sizeof(stuff), stdin) != NULL) 
+        {
+	  p = stuff + strlen(stuff) - 1;
+	  while (p >= stuff && (*p == '\r' || *p == '\n')) {
+	    *p-- = '\0';
+	  }
+
+	  if (strlen(stuff) == 0 || stuff[0] == '#') 
+          {
+	    /* comment or blank line */
+	    text_color_set(DW_COLOR_INFO);
+	    dw_printf("%s\n", stuff);
+	    continue;
+          }
+  	  else 
+	  {
+	    /* Try to process it. */
+
+	    text_color_set(DW_COLOR_REC);
+	    dw_printf("\n%s\n", stuff);	    
+	 
+	    pp = ax25_from_text(stuff, 1);
+	    if (pp != NULL) 
+            {
+	      decode_aprs (pp);
+	      ax25_delete (pp);
+	    }
+	    else 
+	    {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf("\n%s\n", "ERROR - Could not parse input!\n");
+    	    }
+	  }
+	}
+	return (0);
+}
+
+#endif /* TEST */
+
+/* end decode_aprs.c */
diff --git a/decode_aprs.h b/decode_aprs.h
new file mode 100755
index 0000000..68bb3ae
--- /dev/null
+++ b/decode_aprs.h
@@ -0,0 +1,5 @@
+
+/* decode_aprs.h */
+
+extern void decode_aprs (packet_t pp);
+
diff --git a/dedupe.c b/dedupe.c
new file mode 100755
index 0000000..396e279
--- /dev/null
+++ b/dedupe.c
@@ -0,0 +1,243 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011, 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:	dedupe.c
+ *
+ * Purpose:	Avoid transmitting duplicate packets which are too
+ *		close together.
+ *
+ *
+ * Description:	We want to avoid digipeating duplicate packets to
+ *		to help reduce radio channel congestion with 
+ *		redundant information.
+ *		Duplicate packets can occur in several ways:
+ *
+ *		(1) A digipeated packet can loop between 2 or more
+ *			digipeaters.  For example:
+ *
+ *			W1ABC>APRS,WIDE3-3
+ *			W1ABC>APRS,mycall*,WIDE3-2
+ *			W1ABC>APRS,mycall,RPT1*,WIDE3-1
+ *			W1ABC>APRS,mycall,RPT1,mycall*
+ *
+ *		(2) We could hear our own original transmission
+ *			repeated by someone else.  Example:
+ *
+ *			mycall>APRS,WIDE3-3
+ *			mycall>APRS,RPT1*,WIDE3-2
+ *			mycall>APRS,RPT1*,mycall*,WIDE3-1
+ *
+ *		(3) We could hear the same packet from multiple
+ *			digipeaters (with or without the original).
+ *
+ *			W1ABC>APRS,WIDE3-2
+ *			W1ABC>APRS,RPT1*,WIDE3-2
+ *			W1ABC>APRS,RPT2*,WIDE3-2
+ *			W1ABC>APRS,RPT3*,WIDE3-2
+ *
+ *		(4) Someone could be sending the same thing over and 
+ *			over with very little delay in between.
+ *
+ *			W1ABC>APRS,WIDE3-3
+ *			W1ABC>APRS,WIDE3-3
+ *			W1ABC>APRS,WIDE3-3
+ *
+ *		We can catch the first two by looking for 'mycall' in
+ *		the source or digipeater fields.
+ *
+ *		The other two cases require us to keep a record of what
+ *		we transmitted recently and test for duplicates that 
+ *		should be dropped.
+ *		
+ *		Once we have the solution to catch cases (3) and (4)
+ *		there is no reason for the special case of looking for
+ *		mycall.  The same technique catches all four situations.
+ *
+ *		For detecting duplicates, we need to look
+ *			+ source station
+ *			+ destination 
+ *			+ information field
+ *		but NOT the changing list of digipeaters.
+ *
+ *		Typically, only a checksum is kept to reduce memory 
+ *		requirements and amount of compution for comparisons.
+ *		There is a very very small probability that two unrelated 
+ *		packets will result in the same checksum, and the
+ *		undesired dropping of the packet.
+ *
+ * References:	Original APRS specification:
+ *
+ *			TBD...
+ *
+ *		"The New n-N Paradigm"
+ *
+ *			http://www.aprs.org/fix14439.html
+ *		
+ *------------------------------------------------------------------*/
+
+#define DEDUPE_C
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <time.h>
+
+
+#include "ax25_pad.h"
+#include "dedupe.h"
+#include "fcs_calc.h"
+#include "textcolor.h"
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	dedupe_init
+ * 
+ * Purpose:	Initialize the duplicate detection subsystem.
+ *
+ * Input:	ttl	- Number of seconds to retain information
+ *			  about recent transmissions.
+ *	
+ *		
+ * Returns:	None
+ *
+ * Description:	This should be called at application startup.
+ *
+ *		
+ *------------------------------------------------------------------------------*/
+
+static int history_time = 30;		/* Number of seconds to keep information */
+					/* about recent transmissions. */
+
+#define HISTORY_MAX 25			/* Maximum number of transmission */
+					/* records to keep.  If we run out of */
+					/* room the oldest ones are overwritten */
+					/* before they expire. */
+
+static int insert_next;			/* Index, in array below, where next */
+					/* item should be stored. */
+					
+static struct {
+
+	time_t time_stamp;		/* When the packet was transmitted. */
+
+	unsigned short checksum;	/* Some sort of checksum for the */
+					/* source, destination, and information. */
+					/* is is not used anywhere else. */
+
+	short xmit_channel;		/* Radio channel number. */
+
+} history[HISTORY_MAX];
+
+
+void dedupe_init (int ttl)
+{
+	history_time = ttl;
+	insert_next = 0;
+	memset (history, 0, sizeof(history));
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	dedupe_remember
+ * 
+ * Purpose:	Save information about a packet being transmitted so we
+ *		can detect, and avoid, duplicates later.
+ *
+ * Input:	pp	- Pointer to packet object.
+ *		
+ *		chan	- Radio channel for transmission.
+ *		
+ * Returns:	None
+ *
+ * Rambling:	At one time, my thinking is that we want to keep track of
+ *		ALL transmitted packets regardless of origin or type.
+ *
+ *			+ my beacons
+ *			+ anything from a connected application 
+ *			+ anything digipeated
+ *
+ *		The easiest way to catch all cases is to call dedup_remember()
+ *		from inside tq_append().  
+ *
+ *		But I don't think that is the right approach.
+ *		When acting as a KISS TNC, we should just shovel everything
+ *		through and not question what the application is doing.
+ *		If the connected application has a digipeating function,
+ *		it's responsible for those decisions.
+ *
+ *		My current thinking is that dedupe_remember() should be 
+ *		called BEFORE tq_append() in the digipeater case.
+ *
+ *		We should also capture our own beacon transmissions.
+ *		
+ *------------------------------------------------------------------------------*/
+
+void dedupe_remember (packet_t pp, int chan)
+{
+	history[insert_next].time_stamp = time(NULL);
+	history[insert_next].checksum = ax25_dedupe_crc(pp);
+	history[insert_next].xmit_channel = chan;
+
+	insert_next++;
+	if (insert_next >= HISTORY_MAX) {
+	  insert_next = 0;
+	}
+}
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	dedupe_check
+ * 
+ * Purpose:	Check whether this is a duplicate of another sent recently.
+ *
+ * Input:	pp	- Pointer to packet object.
+ *		
+ *		chan	- Radio channel for transmission.
+ *		
+ * Returns:	True if it is a duplicate.
+ *
+ *		
+ *------------------------------------------------------------------------------*/
+
+int dedupe_check (packet_t pp, int chan)
+{
+	unsigned short crc = ax25_dedupe_crc(pp);
+	time_t now = time(NULL);
+	int j;
+
+	for (j=0; j<HISTORY_MAX; j++) {
+	  if (history[j].time_stamp >= now - history_time &&
+	      history[j].checksum == crc && 
+	      history[j].xmit_channel == chan) {
+	    return 1;
+	  }
+	}
+	return 0;
+}
+
+
+/* end dedupe.c */
diff --git a/dedupe.h b/dedupe.h
new file mode 100755
index 0000000..abac2ae
--- /dev/null
+++ b/dedupe.h
@@ -0,0 +1,10 @@
+
+
+void dedupe_init (int ttl);
+
+void dedupe_remember (packet_t pp, int chan);
+
+int dedupe_check (packet_t pp, int chan);
+
+
+/* end dedupe.h */
diff --git a/demod.c b/demod.c
new file mode 100755
index 0000000..695dcab
--- /dev/null
+++ b/demod.c
@@ -0,0 +1,570 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+// 
+//    Copyright (C) 2011,2012,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+// #define DEBUG1 1     /* display debugging info */
+
+// #define DEBUG3 1	/* print carrier detect changes. */
+
+// #define DEBUG4 1	/* capture AFSK demodulator output to log files */
+
+// #define DEBUG5 1	/* capture 9600 output to log files */
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      demod.c
+ *
+ * Purpose:   	Common entry point for multiple types of demodulators.
+ *		
+ * Input:	Audio samples from either a file or the "sound card."
+ *
+ * Outputs:	Calls hdlc_rec_bit() for each bit demodulated.  
+ *
+ *---------------------------------------------------------------*/
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "direwolf.h"
+#include "audio.h"
+#include "demod.h"
+#include "tune.h"
+#include "fsk_demod_state.h"
+#include "fsk_gen_filter.h"
+#include "fsk_fast_filter.h"
+#include "hdlc_rec.h"
+#include "textcolor.h"
+#include "demod_9600.h"
+#include "demod_afsk.h"
+
+
+
+// Properties of the radio channels.
+
+static struct audio_s modem;
+
+// Current state of all the decoders.
+
+static struct demodulator_state_s demodulator_state[MAX_CHANS][MAX_SUBCHANS];
+
+
+#define UPSAMPLE 2
+
+static int sample_sum[MAX_CHANS][MAX_SUBCHANS];
+static int sample_count[MAX_CHANS][MAX_SUBCHANS];
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        demod_init
+ *
+ * Purpose:     Initialize the demodulator(s) used for reception.
+ *
+ * Inputs:      pa		- Pointer to modem_s structure with
+ *				  various parameters for the modem(s).
+ *
+ * Returns:     0 for success, -1 for failure.
+ *		
+ *
+ * Bugs:	This doesn't do much error checking so don't give it
+ *		anything crazy.
+ *
+ *----------------------------------------------------------------*/
+
+int demod_init (struct audio_s *pa)
+{
+	int j;
+	int chan;		/* Loop index over number of radio channels. */
+	int subchan;		/* for each modem for channel. */
+	char profile;
+	//float fc;
+
+	struct demodulator_state_s *D;
+
+
+/*
+ * Save parameters for later use.
+ */
+	memcpy (&modem, pa, sizeof(modem));
+
+	for (chan = 0; chan < modem.num_channels; chan++) {
+
+	  assert (chan >= 0 && chan < MAX_CHANS);
+
+	  switch (modem.modem_type[chan]) {
+
+	    case AFSK:
+/*
+ * Pick a good default demodulator if none specified. 
+ */
+	      if (strlen(modem.profiles[chan]) == 0) {
+
+	        if (modem.baud[chan] < 600) {
+
+	          /* This has been optimized for 300 baud. */
+
+	          strcpy (modem.profiles[chan], "D");
+		  if (modem.samples_per_sec > 40000) {
+		    modem.decimate[chan] = 3;
+		  }
+	        }
+	        else {
+#if __arm__
+	          /* We probably don't have a lot of CPU power available. */
+
+	          if (modem.baud[chan] == FFF_BAUD &&
+		      modem.mark_freq[chan] == FFF_MARK_FREQ && 
+		      modem.space_freq[chan] == FFF_SPACE_FREQ &&
+		      modem.samples_per_sec == FFF_SAMPLES_PER_SEC) {
+
+	            modem.profiles[chan][0] = FFF_PROFILE;
+	            modem.profiles[chan][1] = '\0';
+	          }
+	          else {
+	            strcpy (modem.profiles[chan], "A");
+	          }
+#else
+	          strcpy (modem.profiles[chan], "C");
+#endif
+	        }
+	      }
+
+	      if (modem.decimate[chan] == 0) modem.decimate[chan] = 1;
+
+	      text_color_set(DW_COLOR_DEBUG);
+	      dw_printf ("Channel %d: %d baud, AFSK %d & %d Hz, %s, %d sample rate",
+		    chan, modem.baud[chan], 
+		    modem.mark_freq[chan], modem.space_freq[chan],
+		    modem.profiles[chan],
+		    modem.samples_per_sec);
+	      if (modem.decimate[chan] != 1) 
+	        dw_printf (" / %d", modem.decimate[chan]);
+	      dw_printf (".\n");
+
+	      if (strlen(modem.profiles[chan]) > 1) {
+
+/*
+ * Multiple profiles, usually for 1200 baud.
+ */
+	        assert (modem.num_subchan[chan] == strlen(modem.profiles[chan]));
+	  
+	        for (subchan = 0; subchan < modem.num_subchan[chan]; subchan++) {
+
+	          int mark, space;
+	          assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+	          D = &demodulator_state[chan][subchan];
+
+	          profile = modem.profiles[chan][subchan];
+	          mark = modem.mark_freq[chan];
+	          space = modem.space_freq[chan];
+
+	          if (modem.num_subchan[chan] != 1) {
+	            text_color_set(DW_COLOR_DEBUG);
+	            dw_printf ("        %d.%d: %c %d & %d\n", chan, subchan, profile, mark, space);
+	          }
+      
+	          demod_afsk_init (modem.samples_per_sec / modem.decimate[chan], modem.baud[chan],
+			    mark, space,
+			    profile,
+			    D);
+	        }
+	      }
+	      else {
+/*
+ * Possibly multiple frequency pairs.
+ */
+	  
+	        assert (modem.num_freq[chan] == modem.num_subchan[chan]);
+	        assert (strlen(modem.profiles[chan]) == 1);
+
+	        for (subchan = 0; subchan < modem.num_freq[chan]; subchan++) {
+
+	          int mark, space, k;
+	          assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+	          D = &demodulator_state[chan][subchan];
+
+	          profile = modem.profiles[chan][0];
+
+	          k = subchan * modem.offset[chan] - ((modem.num_subchan[chan] - 1) * modem.offset[chan]) / 2;
+	          mark = modem.mark_freq[chan] + k;
+	          space = modem.space_freq[chan] + k;
+
+	          if (modem.num_subchan[chan] != 1) {
+	            text_color_set(DW_COLOR_DEBUG);
+	            dw_printf ("        %d.%d: %c %d & %d\n", chan, subchan, profile, mark, space);
+	          }
+      
+	          demod_afsk_init (modem.samples_per_sec / modem.decimate[chan], modem.baud[chan],
+			mark, space,
+			profile,
+			D);
+
+	        } 	  /* for subchan */
+	      }	
+	      break;
+
+	    default:	
+
+	      text_color_set(DW_COLOR_DEBUG);
+	      dw_printf ("Channel %d: %d baud, %d sample rate x %d.\n",
+		    chan, modem.baud[chan], 
+		    modem.samples_per_sec, UPSAMPLE);
+
+	      subchan = 0;
+	      D = &demodulator_state[chan][subchan];
+
+	      demod_9600_init (UPSAMPLE * modem.samples_per_sec, modem.baud[chan], D);
+
+	      break;
+
+	  }  /* switch on modulation type. */
+    
+	}     /* for chan ... */
+
+
+
+	for (chan=0; chan<MAX_CHANS; chan++) 
+	{
+	  for (subchan = 0; subchan < modem.num_subchan[chan]; subchan++) {
+	    struct demodulator_state_s *D;
+
+	    assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+	    sample_sum[chan][subchan] = 0;
+	    sample_count[chan][subchan] = subchan;	/* stagger */
+
+	    D = &demodulator_state[chan][subchan];
+
+/* For collecting input signal level. */
+
+	    D->lev_period = modem.samples_per_sec * 0.100;  // Samples in 0.100 seconds.
+
+	  }
+	}
+
+        return (0);
+
+} /* end demod_init */
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        demod_get_sample
+ *
+ * Purpose:     Get one audio sample fromt the sound input source.
+ *
+ * Returns:     -32768 .. 32767 for a valid audio sample.
+ *              256*256 for end of file or other error.
+ *
+ * Global In:	modem.bits_per_sample - So we know whether to 
+ *			read 1 or 2 bytes from audio stream.
+ *
+ * Description:	Grab 1 or two btyes depending on data source.
+ *
+ *		When processing stereo, the caller will call this
+ *		at twice the normal rate to obtain alternating left 
+ *		and right samples.
+ *
+ *----------------------------------------------------------------*/
+
+#define FSK_READ_ERR (256*256)
+
+
+__attribute__((hot))
+int demod_get_sample (void)
+{
+	int x1, x2;
+	signed short sam;	/* short to force sign extention. */
+
+
+	assert (modem.bits_per_sample == 8 || modem.bits_per_sample == 16);
+
+
+	if (modem.bits_per_sample == 8) {
+
+	  x1 = audio_get();	
+	  if (x1 < 0) return(FSK_READ_ERR);
+
+	  assert (x1 >= 0 && x1 <= 255);
+
+	  /* Scale 0..255 into -32k..+32k */
+
+	  sam = (x1 - 128) * 256;
+
+	}
+	else {
+	  x1 = audio_get();	/* lower byte first */
+	  if (x1 < 0) return(FSK_READ_ERR);
+
+	  x2 = audio_get();
+	  if (x2 < 0) return(FSK_READ_ERR);
+
+	  assert (x1 >= 0 && x1 <= 255);
+	  assert (x2 >= 0 && x2 <= 255);
+
+          sam = ( x2 << 8 ) | x1;
+	}
+
+	return (sam);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        demod_process_sample
+ *
+ * Purpose:     (1) Demodulate the AFSK signal.
+ *		(2) Recover clock and data.
+ *
+ * Inputs:	chan	- Audio channel.  0 for left, 1 for right.
+ *		subchan - modem of the channel.
+ *		sam	- One sample of audio.
+ *			  Should be in range of -32768 .. 32767.
+ *
+ * Returns:	None 
+ *
+ * Descripion:	We start off with two bandpass filters tuned to
+ *		the given frequencies.  In the case of VHF packet
+ *		radio, this would be 1200 and 2200 Hz.
+ *
+ *		The bandpass filter amplitudes are compared to 
+ *		obtain the demodulated signal.
+ *
+ *		We also have a digital phase locked loop (PLL)
+ *		to recover the clock and pick out data bits at
+ *		the proper rate.
+ *
+ *		For each recovered data bit, we call:
+ *
+ *			  hdlc_rec (channel, demodulated_bit);
+ *
+ *		to decode HDLC frames from the stream of bits.
+ *
+ * Future:	This could be generalized by passing in the name
+ *		of the function to be called for each bit recovered
+ *		from the demodulator.  For now, it's simply hard-coded.
+ *
+ *--------------------------------------------------------------------*/
+
+
+__attribute__((hot))
+void demod_process_sample (int chan, int subchan, int sam)
+{
+	float fsam, abs_fsam;
+	int k;
+
+
+#if DEBUG4
+	static FILE *demod_log_fp = NULL;
+	static int seq = 0;			/* for log file name */
+#endif
+
+	int j;
+	int demod_data;
+	struct demodulator_state_s *D;
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+	D = &demodulator_state[chan][subchan];
+
+
+#if 1	/* TODO:  common level detection. */
+
+	/* Scale to nice number, TODO: range -1.0 to +1.0, not 2. */
+
+	fsam = sam / 16384.0;
+
+/*
+ * Accumulate measure of the input signal level.
+ */
+	abs_fsam = fsam >= 0 ? fsam : -fsam;
+               
+	if (abs_fsam > D->lev_peak_acc) {
+	  D->lev_peak_acc = abs_fsam;
+	}
+	D->lev_sum_acc += abs_fsam;
+
+	D->lev_count++;
+	if (D->lev_count >= D->lev_period) {
+	  D->lev_prev_peak = D->lev_last_peak;
+          D->lev_last_peak = D->lev_peak_acc;
+          D->lev_peak_acc = 0;
+
+          D->lev_prev_ave = D->lev_last_ave;
+   	  D->lev_last_ave = D->lev_sum_acc / D->lev_count;
+	  D->lev_sum_acc = 0;
+
+	  D->lev_count = 0;
+	}
+
+#endif
+
+/*
+ * Select decoder based on modulation type.
+ */
+
+	switch (modem.modem_type[chan]) {
+
+	  case AFSK:
+
+	    if (modem.decimate[chan] > 1) {
+
+	      sample_sum[chan][subchan] += sam;
+	      sample_count[chan][subchan]++;
+	      if (sample_count[chan][subchan] >= modem.decimate[chan]) {
+  	        demod_afsk_process_sample (chan, subchan, sample_sum[chan][subchan] / modem.decimate[chan], D);
+	        sample_sum[chan][subchan] = 0;
+	        sample_count[chan][subchan] = 0;
+	      }
+	    }
+	    else {
+  	      demod_afsk_process_sample (chan, subchan, sam, D);
+	    }
+	    break;
+
+	  default:
+
+#define ZEROSTUFF 1
+
+	
+#if ZEROSTUFF
+	    /* Literature says this is better if followed */
+	    /* by appropriate low pass filter. */
+	    /* So far, both are same in tests with different */
+	    /* optimal low pass filter parameters. */
+
+	    for (k=1; k<UPSAMPLE; k++) {
+	      demod_9600_process_sample (chan, 0, D);
+	    }
+	    demod_9600_process_sample (chan, sam*UPSAMPLE, D);
+#else
+	    /* Linear interpolation. */
+	    static int prev_sam;
+	    switch (UPSAMPLE) {
+	      case 1:
+	        demod_9600_process_sample (chan, sam);
+
+	        break;
+	      case 2:
+	        demod_9600_process_sample (chan, (prev_sam + sam) / 2, D);
+	        demod_9600_process_sample (chan, sam, D);
+	        break;
+              case 3:
+                demod_9600_process_sample (chan, (2 * prev_sam + sam) / 3, D);
+                demod_9600_process_sample (chan, (prev_sam + 2 * sam) / 3, D);
+                demod_9600_process_sample (chan, sam, D);
+                break;
+              case 4:
+                demod_9600_process_sample (chan, (3 * prev_sam + sam) / 4, D);
+                demod_9600_process_sample (chan, (prev_sam + sam) / 2, D);
+                demod_9600_process_sample (chan, (prev_sam + 3 * sam) / 4, D);
+                demod_9600_process_sample (chan, sam, D);
+                break;
+              default:
+                assert (0);
+                break;
+	    }
+	    prev_sam = sam;
+#endif
+	    break;
+	}
+	return;
+
+} /* end demod_process_sample */
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        fsk_demod_print_agc
+ *
+ * Purpose:     Print information about input signal amplitude.
+ *		This will be useful for adjusting transmitter audio levels.
+ *		We also want to avoid having an input level so high
+ *		that the A/D converter "clips" the signal.
+ *
+ *
+ * Inputs:	chan	- Audio channel.  0 for left, 1 for right.
+ *
+ * Returns:	None 
+ *
+ * Descripion:	Not sure what to use for final form.
+ *		For now display the AGC peaks for both tones.
+ *		This will be called at the end of a frame.
+ *
+ * Future:	Come up with a sensible scale and add command line option.
+ *		Probably makes more sense to return a single number
+ *		and let the caller print it.
+ *		Just an experiment for now.
+ *
+ *--------------------------------------------------------------------*/
+
+#if 0
+void demod_print_agc (int chan, int subchan)
+{
+
+	struct demodulator_state_s *D;
+
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+	D = &demodulator_state[chan][subchan];
+
+	dw_printf ("%d\n", (int)((D->lev_last_peak + D->lev_prev_peak)*50));
+
+
+
+	//dw_printf ("Peak= %.2f, %.2f Ave= %.2f, %.2f AGC M= %.2f / %.2f S= %.2f / %.2f\n", 
+	//	D->lev_last_peak, D->lev_prev_peak, D->lev_last_ave, D->lev_prev_ave,
+	//	D->m_peak, D->m_valley, D->s_peak, D->s_valley);
+
+}
+#endif
+
+/* Resulting scale is 0 to almost 100. */
+/* Cranking up the input level produces no more than 97 or 98. */
+/* We currently produce a message when this goes over 90. */
+
+int demod_get_audio_level (int chan, int subchan) 
+{
+	struct demodulator_state_s *D;
+
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+	D = &demodulator_state[chan][subchan];
+
+	return ( (int) ((D->lev_last_peak + D->lev_prev_peak) * 50 ) );
+}
+
+
+/* end demod.c */
diff --git a/demod.h b/demod.h
new file mode 100755
index 0000000..d1af040
--- /dev/null
+++ b/demod.h
@@ -0,0 +1,16 @@
+
+
+/* demod.h */
+
+#include "audio.h" 	/* for struct audio_s */
+
+
+int demod_init (struct audio_s *pa);
+
+int demod_get_sample (void);
+
+void demod_process_sample (int chan, int subchan, int sam);
+
+void demod_print_agc (int chan, int subchan);
+
+int demod_get_audio_level (int chan, int subchan);
\ No newline at end of file
diff --git a/demod_9600.c b/demod_9600.c
new file mode 100755
index 0000000..cdf3554
--- /dev/null
+++ b/demod_9600.c
@@ -0,0 +1,463 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+// 
+//    Copyright (C) 2011,2012,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+// #define DEBUG5 1	/* capture 9600 output to log files */
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      demod_9600.c
+ *
+ * Purpose:   	Demodulator for scrambled baseband encoding.
+ *		
+ * Input:	Audio samples from either a file or the "sound card."
+ *
+ * Outputs:	Calls hdlc_rec_bit() for each bit demodulated.  
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "direwolf.h"
+#include "tune.h"
+#include "fsk_demod_state.h"
+#include "hdlc_rec.h"
+#include "demod_9600.h"
+#include "textcolor.h"
+#include "dsp.h"
+
+
+/* Add sample to buffer and shift the rest down. */
+
+__attribute__((hot))
+static inline void push_sample (float val, float *buff, int size)
+{
+	int j;
+
+	// TODO: memmove any faster?  
+	for (j = size - 1; j >= 1; j--) {
+	  buff[j] = buff[j-1];
+	}
+	buff[0] = val; 
+}
+
+
+/* FIR filter kernel. */
+
+__attribute__((hot))
+static inline float convolve (const float *data, const float *filter, int filter_size)
+{
+	  float sum = 0;
+	  int j;
+
+	  for (j=0; j<filter_size; j++) {
+	    sum += filter[j] * data[j];
+	  }
+	  return (sum);
+}
+
+/* Automatic gain control. */
+/* Result should settle down to 1 unit peak to peak.  i.e. -0.5 to +0.5 */
+
+__attribute__((hot))
+static inline float agc (float in, float fast_attack, float slow_decay, float *ppeak, float *pvalley)
+{
+	if (in >= *ppeak) {
+	  *ppeak = in * fast_attack + *ppeak * (1. - fast_attack);
+	}
+	else {
+	  *ppeak = in * slow_decay + *ppeak * (1. - slow_decay);
+	}
+
+	if (in <= *pvalley) {
+	  *pvalley = in * fast_attack + *pvalley * (1. - fast_attack);
+	}
+	else  {   
+	  *pvalley = in * slow_decay + *pvalley * (1. - slow_decay);
+	}
+
+	if (*ppeak > *pvalley) {
+	  return ((in - 0.5 * (*ppeak + *pvalley)) / (*ppeak - *pvalley));
+	}
+	return (0.0);
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        demod_9600_init
+ *
+ * Purpose:     Initialize the 9600 baud demodulator.
+ *
+ * Inputs:      samples_per_sec	- Number of samples per second.
+ *				Might be upsampled in hopes of 
+ *				reducing the PLL jitter.
+ *
+ *		baud		- Data rate in bits per second.
+ *
+ *		D		- Address of demodulator state.
+ *
+ * Returns:     None
+ *		
+ *----------------------------------------------------------------*/
+
+void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s *D)
+{	
+	float fc;
+
+	memset (D, 0, sizeof(struct demodulator_state_s));
+
+	//dw_printf ("demod_9600_init(rate=%d, baud=%d, D ptr)\n", samples_per_sec, baud);
+
+        D->pll_step_per_sample = 
+		(int) round(TICKS_PER_PLL_CYCLE * (double) baud / (double)samples_per_sec);
+	
+	D->filter_len_bits =  72 * 9600.0 / (44100.0 * 2.0);		
+	D->lp_filter_size = (int) (( D->filter_len_bits * (float)samples_per_sec / baud) + 0.5);
+#if TUNE_LP_FILTER_SIZE
+	D->lp_filter_size = TUNE_LP_FILTER_SIZE;
+#endif
+
+	D->lpf_baud = 0.59;	
+#ifdef TUNE_LPF_BAUD
+	D->lpf_baud = TUNE_LPF_BAUD;
+#endif	
+
+	D->agc_fast_attack = 0.080;	
+#ifdef TUNE_AGC_FAST
+	D->agc_fast_attack = TUNE_AGC_FAST;
+#endif
+
+	D->agc_slow_decay = 0.00012;
+#ifdef TUNE_AGC_SLOW
+	D->agc_slow_decay = TUNE_AGC_SLOW;
+#endif
+
+	D->pll_locked_inertia = 0.88;
+	D->pll_searching_inertia = 0.67;
+
+#if defined(TUNE_PLL_LOCKED) && defined(TUNE_PLL_SEARCHING)
+	D->pll_locked_inertia = TUNE_PLL_LOCKED;
+	D->pll_searching_inertia = TUNE_PLL_SEARCHING;
+#endif
+
+	fc = (float)baud * D->lpf_baud / (float)samples_per_sec;
+
+	//dw_printf ("demod_9600_init: call gen_lowpass(fc=%.2f, , size=%d, )\n", fc, D->lp_filter_size);
+
+	gen_lowpass (fc, D->lp_filter, D->lp_filter_size, BP_WINDOW_HAMMING);
+
+} /* end fsk_demod_init */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        demod_9600_process_sample
+ *
+ * Purpose:     (1) Filter & slice the signal.
+ *		(2) Descramble it.
+ *		(2) Recover clock and data.
+ *
+ * Inputs:	chan	- Audio channel.  0 for left, 1 for right.
+ *
+ *		sam	- One sample of audio.
+ *			  Should be in range of -32768 .. 32767.
+ *
+ * Returns:	None 
+ *
+ * Descripion:	"9600 baud" packet is FSK for an FM voice transceiver.
+ *		By the time it gets here, it's really a baseband signal.
+ *		At one extreme, we could have a 4800 Hz square wave.
+ *		A the other extreme, we could go a considerable number
+ *		of bit times without any transitions.
+ *
+ *		The trick is to extract the digital data which has
+ *		been distorted by going thru voice transceivers not
+ *		intended to pass this sort of "audio" signal.
+ *
+ *		Data is "scrambled" to reduce the amount of DC bias.
+ *		The data stream must be unscrambled at the receiving end.
+ *
+ *		We also have a digital phase locked loop (PLL)
+ *		to recover the clock and pick out data bits at
+ *		the proper rate.
+ *
+ *		For each recovered data bit, we call:
+ *
+ *			  hdlc_rec (channel, demodulated_bit);
+ *
+ *		to decode HDLC frames from the stream of bits.
+ *
+ * Future:	This could be generalized by passing in the name
+ *		of the function to be called for each bit recovered
+ *		from the demodulator.  For now, it's simply hard-coded.
+ *
+ * References:	9600 Baud Packet Radio Modem Design
+ *		http://www.amsat.org/amsat/articles/g3ruh/109.html
+ *
+ *		The KD2BD 9600 Baud Modem
+ *		http://www.amsat.org/amsat/articles/kd2bd/9k6modem/
+ *
+ *		9600 Baud Packet Handbook
+ * 		ftp://ftp.tapr.org/general/9600baud/96man2x0.txt
+ *
+ *
+ * TODO:	This works in a simulated environment but it has not yet
+ *		been successfully tested for interoperability with 
+ *		other systems over the air.
+ *		That's why it is not mentioned in documentation.
+ *
+ *		The signal from the radio speaker does NOT have 
+ *		enough bandwidth and the waveform is hopelessly distorted.
+ *		It will be necessary to obtain a signal right after
+ *		the discriminator of the receiver.
+ *		It will probably also be necessary to tap directly into
+ *		the modulator, bypassing the microphone amplifier.
+ *
+ *--------------------------------------------------------------------*/
+
+
+__attribute__((hot))
+void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D)
+{
+
+	float fsam;
+	float abs_fsam;
+	float amp;
+	float demod_out;
+
+#if DEBUG5
+	static FILE *demod_log_fp = NULL;
+	static int seq = 0;			/* for log file name */
+#endif
+
+	int j;
+	int subchan = 0;
+	int demod_data;					/* Still scrambled. */
+	static int descram;				/* Data bit de-scrambled. */
+
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+
+/* 
+ * Filters use last 'filter_size' samples.
+ *
+ * First push the older samples down. 
+ *
+ * Finally, put the most recent at the beginning.
+ *
+ * Future project?  Rather than shifting the samples,
+ * it might be faster to add another variable to keep
+ * track of the most recent sample and change the 
+ * indexing in the later loops that multipy and add.
+ */
+
+	/* Scale to nice number, range -1.0 to +1.0. */
+
+	fsam = sam / 32768.0;
+
+	push_sample (fsam, D->raw_cb, D->lp_filter_size);
+
+/*
+ * Low pass filter to reduce noise yet pass the data. 
+ */
+
+	amp = convolve (D->raw_cb, D->lp_filter, D->lp_filter_size);
+
+/* 
+ * The input level can vary greatly.
+ * More importantly, there could be a DC bias which we need to remove.
+ *
+ * Normalize the signal with automatic gain control (AGC). 
+ * This works by looking at the minimum and maximum signal peaks
+ * and scaling the results to be roughly in the -1.0 to +1.0 range.
+ */
+
+	demod_out = 2.0 * agc (amp, D->agc_fast_attack, D->agc_slow_decay, &(D->m_peak), &(D->m_valley));
+
+//dw_printf ("peak=%.2f valley=%.2f amp=%.2f norm=%.2f\n", D->m_peak, D->m_valley, amp, norm);
+
+	/* Throw in a little Hysteresis??? */
+	/* (Not to be confused with Hysteria.) */
+
+	if (demod_out > 0.01) {
+	  demod_data = 1;
+	}
+	else if (demod_out < -0.01) {
+	  demod_data = 0;
+	} 
+	else {
+	  demod_data = D->prev_demod_data;
+	}
+
+
+/*
+ * Next, a PLL is used to sample near the centers of the data bits.
+ *
+ * D->data_clock_pll is a SIGNED 32 bit variable.
+ * When it overflows from a large positive value to a negative value, we 
+ * sample a data bit from the demodulated signal.
+ *
+ * Ideally, the the demodulated signal transitions should be near
+ * zero we we sample mid way between the transitions.
+ *
+ * Nudge the PLL by removing some small fraction from the value of 
+ * data_clock_pll, pushing it closer to zero.
+ * 
+ * This adjustment will never change the sign so it won't cause
+ * any erratic data bit sampling.
+ *
+ * If we adjust it too quickly, the clock will have too much jitter.
+ * If we adjust it too slowly, it will take too long to lock on to a new signal.
+ *
+ * I don't think the optimal value will depend on the audio sample rate
+ * because this happens for each transition from the demodulator.
+ *
+ * This was optimized for 1200 baud AFSK.  There might be some opportunity
+ * for improvement here.
+ */
+	D->prev_d_c_pll = D->data_clock_pll;
+	D->data_clock_pll += D->pll_step_per_sample;
+
+	if (D->data_clock_pll < 0 && D->prev_d_c_pll > 0) {
+
+	  /* Overflow. */
+
+/*
+ * At this point, we need to descramble the data as
+ * in hardware based designs by G3RUH and K9NG.
+ *
+ * http://www.amsat.org/amsat/articles/g3ruh/109/fig03.gif
+ */
+
+	  //assert (modem.modem_type[chan] == SCRAMBLE);
+
+	  //if (modem.modem_type[chan] == SCRAMBLE) {
+
+
+//  TODO:  This needs to be rearranged to allow attempted "fixing"
+//  	of corrupted bits later.  We need to store the original 
+//	received bits and do the descrambling after attempted
+//	repairs.  However, we also need to descramble now to 
+//	detect the flag sequences.
+
+
+	    descram = descramble (demod_data, &(D->lfsr));
+#if SLICENDICE
+	    // TODO: Needs more thought. 
+	    // Does it even make sense to remember demod_out in this case?
+	    // We would need to do the re-thresholding before descrambling.
+	    //hdlc_rec_bit_sam (chan, subchan, descram, descram ? 1.0 : -1.0);
+#else
+
+// TODO: raw received bit and true later.
+
+	    hdlc_rec_bit (chan, subchan, descram, 0, D->lfsr);
+
+#endif
+
+	    //D->prev_descram = descram;
+	  //}
+	  //else {
+	    /* Baseband signal for completeness - not in common use. */
+#if SLICENDICE
+	    //hdlc_rec_bit_sam (chan, subchan, demod_data, demod_data ? 1.0 : -1.0);
+#else
+	    //hdlc_rec_bit (chan, subchan, demod_data);
+#endif
+	  //}
+	}
+
+        if (demod_data != D->prev_demod_data) {
+
+	  // Note:  Test for this demodulator, not overall for channel.
+
+	  if (hdlc_rec_data_detect_1 (chan, subchan)) {
+	    D->data_clock_pll = (int)(D->data_clock_pll * D->pll_locked_inertia);
+	  }
+	  else {
+	    D->data_clock_pll = (int)(D->data_clock_pll * D->pll_searching_inertia);
+	  }
+	}
+
+
+#if DEBUG5
+
+	//if (chan == 0) {
+	if (hdlc_rec_data_detect_1 (chan,subchan)) {
+	
+	  char fname[30];
+
+	  
+	  if (demod_log_fp == NULL) {
+	    seq++;
+	    sprintf (fname, "demod96/%04d.csv", seq);
+	    if (seq == 1) mkdir ("demod96"
+#ifndef __WIN32__
+					, 0777
+#endif
+						);
+
+	    demod_log_fp = fopen (fname, "w");
+	    text_color_set(DW_COLOR_DEBUG);
+	    dw_printf ("Starting 9600 decoder log file %s\n", fname);
+	    fprintf (demod_log_fp, "Audio, Peak, Valley, Demod, SData, Descram, Clock\n");
+	  }
+	  fprintf (demod_log_fp, "%.3f, %.3f, %.3f, %.3f, %.2f, %.2f, %.2f\n", 
+			0.5 * fsam + 3.5,  
+			0.5 * D->m_peak + 3.5,
+			0.5 * D->m_valley + 3.5,
+			0.5 * demod_out + 2.0,
+			demod_data ? 1.35 : 1.0,  
+			descram ? .9 : .55,  
+			(D->data_clock_pll & 0x80000000) ? .1 : .45);
+	}
+	else {
+	  if (demod_log_fp != NULL) {
+	    fclose (demod_log_fp);
+	    demod_log_fp = NULL;
+	  }
+	}
+	//}
+
+#endif
+
+
+/*
+ * Remember demodulator output (pre-descrambling) so we can compare next time
+ * for the DPLL sync.
+ */
+	D->prev_demod_data = demod_data;
+
+} /* end demod_9600_process_sample */
+
+
+
+/* end demod_9600.c */
diff --git a/demod_9600.h b/demod_9600.h
new file mode 100755
index 0000000..486e597
--- /dev/null
+++ b/demod_9600.h
@@ -0,0 +1,21 @@
+
+
+/* demod_9600.h */
+
+void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s *D);
+
+void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D);
+
+
+
+
+/* Undo data scrambling for 9600 baud. */
+
+static inline int descramble (int in, int *state)
+{
+	int out;
+
+	out = (in ^ (*state >> 16) ^ (*state >> 11)) & 1;
+	*state = (*state << 1) | (in & 1);
+	return (out);
+}
diff --git a/demod_afsk.c b/demod_afsk.c
new file mode 100755
index 0000000..fb7e8f2
--- /dev/null
+++ b/demod_afsk.c
@@ -0,0 +1,977 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+// 
+//    Copyright (C) 2011,2012,2013,2014  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+// #define DEBUG1 1     /* display debugging info */
+
+// #define DEBUG3 1	/* print carrier detect changes. */
+
+// #define DEBUG4 1	/* capture AFSK demodulator output to log files */
+
+// #define DEBUG5 1	/* capture 9600 output to log files */
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      demod_afsk.c
+ *
+ * Purpose:   	Demodulator for Audio Frequency Shift Keying (AFSK).
+ *		
+ * Input:	Audio samples from either a file or the "sound card."
+ *
+ * Outputs:	Calls hdlc_rec_bit() for each bit demodulated.  
+ *
+ *---------------------------------------------------------------*/
+
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "direwolf.h"
+#include "audio.h"
+//#include "fsk_demod.h"
+//#include "gen_tone.h"
+#include "tune.h"
+#include "fsk_demod_state.h"
+#include "fsk_gen_filter.h"
+#include "hdlc_rec.h"
+#include "textcolor.h"
+#include "demod_afsk.h"
+#include "dsp.h"
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+
+
+
+/* Quick approximation to sqrt(x*x+y*y) */
+/* No benefit for regular PC. */
+/* Should help with microcomputer platform. */
+
+
+__attribute__((hot))
+static inline float z (float x, float y)
+{
+        x = fabsf(x);
+        y = fabsf(y);
+
+        if (x > y) {
+          return (x * .941246 + y * .41);
+        }
+        else {
+          return (y * .941246 + x * .41);
+        }
+}
+
+/* Add sample to buffer and shift the rest down. */
+
+__attribute__((hot))
+static inline void push_sample (float val, float *buff, int size)
+{
+	int j;
+
+	// TODO: memmove any faster?  
+	for (j = size - 1; j >= 1; j--) {
+	  buff[j] = buff[j-1];
+	}
+	buff[0] = val; 
+}
+
+
+/* FIR filter kernel. */
+
+__attribute__((hot))
+static inline float convolve (const float *data, const float *filter, int filter_size)
+{
+	  float sum = 0;
+	  int j;
+
+	  for (j=0; j<filter_size; j++) {
+	    sum += filter[j] * data[j];
+	  }
+	  return (sum);
+}
+
+/* Automatic gain control. */
+/* Result should settle down to 1 unit peak to peak.  i.e. -0.5 to +0.5 */
+
+__attribute__((hot))
+static inline float agc (float in, float fast_attack, float slow_decay, float *ppeak, float *pvalley)
+{
+	if (in >= *ppeak) {
+	  *ppeak = in * fast_attack + *ppeak * (1. - fast_attack);
+	}
+	else {
+	  *ppeak = in * slow_decay + *ppeak * (1. - slow_decay);
+	}
+
+	if (in <= *pvalley) {
+	  *pvalley = in * fast_attack + *pvalley * (1. - fast_attack);
+	}
+	else  {   
+	  *pvalley = in * slow_decay + *pvalley * (1. - slow_decay);
+	}
+
+	if (*ppeak > *pvalley) {
+	  return ((in - 0.5 * (*ppeak + *pvalley)) / (*ppeak - *pvalley));
+	}
+	return (0.0);
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        demod_afsk_init
+ *
+ * Purpose:     Initialization for an AFSK demodulator.
+ *		Select appropriate parameters and set up filters.
+ *
+ * Inputs:   	samples_per_sec
+ *		baud
+ *		mark_freq
+ *		space_freq
+ *	
+ *		D		- Pointer to demodulator state for given channel.
+ *
+ * Outputs:	D->ms_filter_size
+ *		D->m_sin_table[] 
+ *		D->m_cos_table[]
+ *		D->s_sin_table[] 
+ *		D->s_cos_table[]
+ *
+ * Returns:     None.
+ *		
+ * Bugs:	This doesn't do much error checking so don't give it
+ *		anything crazy.
+ *
+ *----------------------------------------------------------------*/
+
+void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
+			int space_freq, char profile, struct demodulator_state_s *D)
+{
+	
+	int j;
+	
+	memset (D, 0, sizeof(struct demodulator_state_s));
+
+#if DEBUG1
+	dw_printf ("demod_afsk_init (rate=%d, baud=%d, mark=%d, space=%d, profile=%c\n",
+		samples_per_sec, baud, mark_freq, space_freq, profile);
+#endif
+				
+#ifdef TUNE_PROFILE
+	profile = TUNE_PROFILE;
+#endif
+
+	if (toupper(profile) == 'F') {
+
+	  if (baud != DEFAULT_BAUD ||
+	      mark_freq != DEFAULT_MARK_FREQ ||
+	      space_freq!= DEFAULT_SPACE_FREQ ||
+	      samples_per_sec != DEFAULT_SAMPLES_PER_SEC) {
+
+	    	text_color_set(DW_COLOR_INFO);
+		dw_printf ("Note: Decoder 'F' works only for %d baud, %d/%d tones, %d samples/sec.\n",
+			DEFAULT_BAUD, DEFAULT_MARK_FREQ, DEFAULT_SPACE_FREQ, DEFAULT_SAMPLES_PER_SEC);
+		dw_printf ("Using Decoder 'A' instead.\n");
+		profile = 'A';
+	  }
+	}
+
+	if (profile == 'a' || profile == 'A' || profile == 'f' || profile == 'F') {
+
+		/* Original.  52 taps, truncated bandpass, IIR lowpass */
+		/* 'F' is the fast version for low end processors. */
+		/* It is a special case that works only for a particular */
+		/* baud rate, tone pair, and sampling rate. */
+
+	    D->filter_len_bits = 1.415;		/* 52 @ 44100, 1200 */
+	    D->bp_window = BP_WINDOW_TRUNCATED;
+	    D->lpf_use_fir = 0;
+	    D->lpf_iir = 0.195;
+	    D->lpf_baud = 0;
+	    D->agc_fast_attack = 0.250;		
+	    D->agc_slow_decay = 0.00012;
+	    D->hysteresis = 0.005;
+	    D->pll_locked_inertia = 0.700;
+	    D->pll_searching_inertia = 0.580;
+	}
+	else if (profile == 'b' || profile == 'B') {
+
+		/* Original bandpass.  Use FIR lowpass instead. */
+
+	    D->filter_len_bits = 1.415;		/* 52 @ 44100, 1200 */
+	    D->bp_window = BP_WINDOW_TRUNCATED;
+	    D->lpf_use_fir = 1;
+	    D->lpf_iir = 0;
+	    D->lpf_baud = 1.09;
+	    D->agc_fast_attack = 0.370;		
+	    D->agc_slow_decay = 0.00014;
+	    D->hysteresis = 0.003;
+	    D->pll_locked_inertia = 0.620;
+	    D->pll_searching_inertia = 0.350;
+	}
+	else if (profile == 'c' || profile == 'C') {
+
+		/* Cosine window, 76 taps for bandpass, FIR lowpass. */
+
+	    D->filter_len_bits = 2.068;		/* 76 @ 44100, 1200 */
+	    D->bp_window = BP_WINDOW_COSINE;
+	    D->lpf_use_fir = 1;
+	    D->lpf_iir = 0;
+	    D->lpf_baud = 1.09;
+	    D->agc_fast_attack = 0.495;		
+	    D->agc_slow_decay = 0.00022;
+	    D->hysteresis = 0.005;
+	    D->pll_locked_inertia = 0.620;
+	    D->pll_searching_inertia = 0.350;
+	}
+	else if (profile == 'd' || profile == 'D') {
+
+		/* Prefilter, Cosine window, FIR lowpass. Tweeked for 300 baud. */
+
+	    D->use_prefilter = 1;		/* first, a bandpass filter. */
+	    D->prefilter_baud = 0.87;		/* Cosine window. */
+	    D->filter_len_bits = 1.857;		/* 91 @ 44100/3, 300 */
+	    D->bp_window = BP_WINDOW_COSINE;
+	    D->lpf_use_fir = 1;
+	    D->lpf_iir = 0;
+	    D->lpf_baud = 1.10;
+	    D->agc_fast_attack = 0.495;		
+	    D->agc_slow_decay = 0.00022;
+	    D->hysteresis = 0.027;
+	    D->pll_locked_inertia = 0.620;
+	    D->pll_searching_inertia = 0.350;
+	}
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Invalid filter profile = %c\n", profile);
+	  exit (1);
+	}
+
+
+#if defined(TUNE_AGC_FAST) && defined(TUNE_AGC_SLOW)
+	D->agc_fast_attack = TUNE_AGC_FAST;
+	D->agc_slow_decay = TUNE_AGC_SLOW;
+#endif
+#ifdef TUNE_HYST
+	D->hysteresis = TUNE_HYST;
+#endif
+#if defined(TUNE_PLL_LOCKED) && defined(TUNE_PLL_SEARCHING)
+	D->pll_locked_inertia = TUNE_PLL_LOCKED;
+	D->pll_searching_inertia = TUNE_PLL_SEARCHING;
+#endif
+#ifdef TUNE_LPF_BAUD
+	D->lpf_baud = TUNE_LPF_BAUD;
+#endif
+#ifdef TUNE_PRE_BAUD
+	D->prefilter_baud = TUNE_PRE_BAUD;
+#endif
+
+/*
+ * Calculate constants used for timing.
+ * The audio sample rate must be at least a few times the data rate.
+ */
+
+	D->pll_step_per_sample = (int) round((TICKS_PER_PLL_CYCLE * (double)baud) / ((double)samples_per_sec));
+
+
+/*
+ * My initial guess at length of filter was about one bit time.
+ * By trial and error, the optimal value was found to somewhat longer.
+ * This was optimized for 44,100 sample rate, 1200 baud, 1200/2200 Hz.
+ * More experimentation is needed for other situations.
+ */
+
+	D->ms_filter_size = (int) round( D->filter_len_bits * (float)samples_per_sec / (float)baud );
+	  	 
+/* Experiment with other sizes. */
+
+#if defined(TUNE_MS_FILTER_SIZE)
+	D->ms_filter_size = TUNE_MS_FILTER_SIZE;
+#endif
+	D->lp_filter_size = D->ms_filter_size;
+
+	assert (D->ms_filter_size >= 4);
+
+	if (D->ms_filter_size > MAX_FILTER_SIZE) 
+	{
+	  text_color_set (DW_COLOR_ERROR);
+	  dw_printf ("Calculated filter size of %d is too large.\n", D->ms_filter_size);
+	  dw_printf ("Decrease the audio sample rate or increase the baud rate or\n");
+	  dw_printf ("recompile the application with MAX_FILTER_SIZE larger than %d.\n",
+							MAX_FILTER_SIZE);
+	  exit (1);
+	}
+
+
+/* 
+ * For narrow AFSK (e.g. 200 Hz shift), it might be beneficial to 
+ * have a bandpass filter before the mark/space detector.
+ * For now, make it the same number of taps for simplicity.
+ */
+
+	if (D->use_prefilter) {
+	  float f1, f2;
+
+	  f1 = MIN(mark_freq,space_freq) - D->prefilter_baud * baud;
+	  f2 = MAX(mark_freq,space_freq) + D->prefilter_baud * baud;
+#if 0
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("Generating prefilter %.0f to %.0f Hz.\n", f1, f2);
+#endif
+	  f1 = f1 / (float)samples_per_sec;
+	  f2 = f2 / (float)samples_per_sec;
+	  
+	  //gen_bandpass (f1, f2, D->pre_filter, D->ms_filter_size, BP_WINDOW_HAMMING);
+	  //gen_bandpass (f1, f2, D->pre_filter, D->ms_filter_size, BP_WINDOW_BLACKMAN);
+	  gen_bandpass (f1, f2, D->pre_filter, D->ms_filter_size, BP_WINDOW_COSINE);
+	}
+
+/*
+ * Filters for detecting mark and space tones.
+ */
+#if DEBUG1
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("%s:  \n", __FILE__);
+	  dw_printf ("%d baud, %d samples_per_sec\n", baud, samples_per_sec);
+	  dw_printf ("AFSK %d & %d Hz\n", mark_freq, space_freq);
+	  dw_printf ("spll_step_per_sample = %d = 0x%08x\n", D->pll_step_per_sample, D->pll_step_per_sample);
+	  dw_printf ("D->ms_filter_size = %d = 0x%08x\n", D->ms_filter_size, D->ms_filter_size);
+	  dw_printf ("\n");
+	  dw_printf ("Mark\n");
+	  dw_printf ("   j     shape   M sin   M cos \n");
+#endif
+
+          for (j=0; j<D->ms_filter_size; j++) {
+	    float am;
+	    float center;
+	    float shape = 1;		/* Shape is an attempt to smooth out the */
+					/* abrupt edges in hopes of reducing */
+					/* overshoot and ringing. */
+					/* My first thought was to use a cosine shape. */
+					/* Should investigate Hamming and Blackman */
+					/* windows mentioned in the literature. */
+					/* http://en.wikipedia.org/wiki/Window_function */
+
+	    center = 0.5 * (D->ms_filter_size - 1);
+	    am = ((float)(j - center) / (float)samples_per_sec) * ((float)mark_freq) * (2 * M_PI);
+
+	    shape = window (D->bp_window, D->ms_filter_size, j);
+
+	    D->m_sin_table[j] = sin(am) * shape;
+  	    D->m_cos_table[j] = cos(am) * shape;
+
+#if DEBUG1
+	    dw_printf ("%6d  %6.2f  %6.2f  %6.2f\n", j, shape, D->m_sin_table[j], D->m_cos_table[j]) ;
+#endif
+          }
+
+
+#if DEBUG1
+	  text_color_set(DW_COLOR_DEBUG);
+
+	  dw_printf ("Space\n");
+	  dw_printf ("   j     shape   S sin   S cos\n");
+#endif
+          for (j=0; j<D->ms_filter_size; j++) {
+	    float as;
+	    float center;
+	    float shape = 1;
+
+	    center = 0.5 * (D->ms_filter_size - 1);
+	    as = ((float)(j - center) / (float)samples_per_sec) * ((float)space_freq) * (2 * M_PI);
+
+	    shape = window (D->bp_window, D->ms_filter_size, j);
+
+	    D->s_sin_table[j] = sin(as) * shape;
+  	    D->s_cos_table[j] = cos(as) * shape;
+
+#if DEBUG1
+	    dw_printf ("%6d  %6.2f  %6.2f  %6.2f\n", j, shape, D->s_sin_table[j], D->s_cos_table[j] ) ;
+#endif
+          }
+
+
+/* Do we want to normalize for unity gain? */
+
+
+/*
+ * Now the lowpass filter.
+ * I thought we'd want a cutoff of about 0.5 the baud rate 
+ * but it turns out about 1.1x is better.  Still investigating...
+ */
+
+	if (D->lpf_use_fir) {
+	  float fc;
+	  fc = baud * D->lpf_baud / (float)samples_per_sec;
+	  gen_lowpass (fc, D->lp_filter, D->lp_filter_size, BP_WINDOW_TRUNCATED);
+	}
+
+/*
+ * A non-whole number of cycles results in a DC bias. 
+ * Let's see if it helps to take it out.
+ * Actually makes things worse:  20 fewer decoded.
+ * Might want to try again after EXPERIMENTC.
+ */
+
+#if 0
+#ifndef AVOID_FLOATING_POINT
+
+failed experiment
+
+	dc_bias = 0;
+        for (j=0; j<D->ms_filter_size; j++) {
+	  dc_bias += D->m_sin_table[j];
+	}
+        for (j=0; j<D->ms_filter_size; j++) {
+	  D->m_sin_table[j] -= dc_bias / D->ms_filter_size;
+	}
+
+	dc_bias = 0;
+        for (j=0; j<D->ms_filter_size; j++) {
+	  dc_bias += D->m_cos_table[j];
+	}
+        for (j=0; j<D->ms_filter_size; j++) {
+	  D->m_cos_table[j] -= dc_bias / D->ms_filter_size;
+	}
+
+
+	dc_bias = 0;
+        for (j=0; j<D->ms_filter_size; j++) {
+	  dc_bias += D->s_sin_table[j];
+	}
+        for (j=0; j<D->ms_filter_size; j++) {
+	  D->s_sin_table[j] -= dc_bias / D->ms_filter_size;
+	}
+
+	dc_bias = 0;
+        for (j=0; j<D->ms_filter_size; j++) {
+	  dc_bias += D->s_cos_table[j];
+	}
+        for (j=0; j<D->ms_filter_size; j++) {
+	  D->s_cos_table[j] -= dc_bias / D->ms_filter_size;
+	}
+
+#endif
+#endif
+
+}  /* fsk_gen_filter */
+
+
+#if GEN_FFF
+
+
+
+// Properties of the radio channels.
+
+static struct audio_s modem;
+
+
+// Filters will be stored here. 
+
+static struct demodulator_state_s ds;
+
+
+#define SPARSE 3
+
+
+static void emit_macro (char *name, int size, float *coeff)
+{
+	int i;
+
+	dw_printf ("#define %s(x) \\\n", name);
+
+	for (i=SPARSE/2; i<size; i+=SPARSE) {
+	  dw_printf ("\t%c (%.6f * x[%d]) \\\n", (i==0 ? ' ' : '+'), coeff[i], i);
+	}
+	dw_printf ("\n");
+}
+
+int main ()
+{
+	//int n;
+	char fff_profile;
+
+	fff_profile = 'F';		
+
+	memset (&modem, 0, sizeof(modem));
+	memset (&ds, 0, sizeof(ds));
+
+	modem.num_channels = 1;
+	modem.samples_per_sec = DEFAULT_SAMPLES_PER_SEC;
+	modem.mark_freq[0] = DEFAULT_MARK_FREQ;
+	modem.space_freq[0] = DEFAULT_SPACE_FREQ;
+	modem.baud[0] = DEFAULT_BAUD;
+ 	modem.num_subchan[0] = 1;
+
+
+	demod_afsk_init (modem.samples_per_sec, modem.baud[0],
+			modem.mark_freq[0], modem.space_freq[0], fff_profile, &ds);
+	
+	printf ("/* This is an automatically generated file.  Do not edit. */\n");
+	printf ("\n");
+	printf ("#define FFF_SAMPLES_PER_SEC %d\n", modem.samples_per_sec);
+	printf ("#define FFF_BAUD %d\n", modem.baud[0]);
+	printf ("#define FFF_MARK_FREQ %d\n", modem.mark_freq[0]);
+	printf ("#define FFF_SPACE_FREQ %d\n", modem.space_freq[0]);
+	printf ("#define FFF_PROFILE '%c'\n", fff_profile);
+	printf ("\n");
+
+	emit_macro ("CALC_M_SUM1", ds.ms_filter_size, ds.m_sin_table);
+	emit_macro ("CALC_M_SUM2", ds.ms_filter_size, ds.m_cos_table);
+	emit_macro ("CALC_S_SUM1", ds.ms_filter_size, ds.s_sin_table);
+	emit_macro ("CALC_S_SUM2", ds.ms_filter_size, ds.s_cos_table);
+
+	exit(0);
+}
+
+#endif
+
+
+
+#ifndef GEN_FFF
+
+/* Optimization for slow processors. */
+
+#include "fsk_fast_filter.h"
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        demod_afsk_process_sample
+ *
+ * Purpose:     (1) Demodulate the AFSK signal.
+ *		(2) Recover clock and data.
+ *
+ * Inputs:	chan	- Audio channel.  0 for left, 1 for right.
+ *		subchan - modem of the channel.
+ *		sam	- One sample of audio.
+ *			  Should be in range of -32768 .. 32767.
+ *
+ * Returns:	None 
+ *
+ * Descripion:	We start off with two bandpass filters tuned to
+ *		the given frequencies.  In the case of VHF packet
+ *		radio, this would be 1200 and 2200 Hz.
+ *
+ *		The bandpass filter amplitudes are compared to 
+ *		obtain the demodulated signal.
+ *
+ *		We also have a digital phase locked loop (PLL)
+ *		to recover the clock and pick out data bits at
+ *		the proper rate.
+ *
+ *		For each recovered data bit, we call:
+ *
+ *			  hdlc_rec (channel, demodulated_bit);
+ *
+ *		to decode HDLC frames from the stream of bits.
+ *
+ * Future:	This could be generalized by passing in the name
+ *		of the function to be called for each bit recovered
+ *		from the demodulator.  For now, it's simply hard-coded.
+ *
+ *--------------------------------------------------------------------*/
+
+
+__attribute__((hot))
+void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D)
+{
+	float fsam, abs_fsam;
+	float m_sum1, m_sum2, s_sum1, s_sum2;
+	float m_amp, s_amp;
+	float m_norm, s_norm;
+	float demod_out;
+#if DEBUG4
+	static FILE *demod_log_fp = NULL;
+	static int seq = 0;			/* for log file name */
+#endif
+
+	int j;
+	int demod_data;
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+
+
+
+
+/* 
+ * Filters use last 'filter_size' samples.
+ *
+ * First push the older samples down. 
+ *
+ * Finally, put the most recent at the beginning.
+ *
+ * Future project?  Can we do better than shifting each time?
+ */
+
+	/* Scale to nice number, TODO: range -1.0 to +1.0, not 2. */
+
+	fsam = sam / 16384.0;
+
+/*
+ * Accumulate measure of the input signal level.
+ */
+	abs_fsam = fsam >= 0 ? fsam : -fsam;
+ 
+// TODO:  move to common code
+              
+	if (abs_fsam > D->lev_peak_acc) {
+	  D->lev_peak_acc = abs_fsam;
+	}
+	D->lev_sum_acc += abs_fsam;
+
+	D->lev_count++;
+	if (D->lev_count >= D->lev_period) {
+	  D->lev_prev_peak = D->lev_last_peak;
+          D->lev_last_peak = D->lev_peak_acc;
+          D->lev_peak_acc = 0;
+
+          D->lev_prev_ave = D->lev_last_ave;
+   	  D->lev_last_ave = D->lev_sum_acc / D->lev_count;
+	  D->lev_sum_acc = 0;
+
+	  D->lev_count = 0;
+	}
+
+/*
+ * Optional bandpass filter before the mark/space discriminator.
+ */
+
+	if (D->use_prefilter) {
+	  float cleaner;
+
+	  push_sample (fsam, D->raw_cb, D->ms_filter_size);
+	  cleaner = convolve (D->raw_cb, D->pre_filter, D->ms_filter_size);
+	  push_sample (cleaner, D->ms_in_cb, D->ms_filter_size);
+	}
+	else {
+	  push_sample (fsam, D->ms_in_cb, D->ms_filter_size);
+	}
+
+/*
+ * Next we have bandpass filters for the mark and space tones.
+ *
+ * This takes a lot of computation.
+ * It's not a problem on a typical (Intel x86 based) PC.
+ * Dire Wolf takes only about 2 or 3% of the CPU time.
+ *
+ * It might be too much for a little microcomputer to handle.
+ *
+ * Here we have an optimized case for the default values.
+ */
+
+
+
+// TODO:   How do we test for profile F here?
+
+	if (0) {
+	//if (toupper(modem.profiles[chan][subchan]) == toupper(FFF_PROFILE)) {
+
+				/* ========== Faster for default values on slower processors. ========== */
+
+	  m_sum1 = CALC_M_SUM1(D->ms_in_cb);
+	  m_sum2 = CALC_M_SUM2(D->ms_in_cb);
+	  m_amp = z(m_sum1,m_sum2);
+
+	  s_sum1 = CALC_S_SUM1(D->ms_in_cb);
+	  s_sum2 = CALC_S_SUM2(D->ms_in_cb);
+	  s_amp = z(s_sum1,s_sum2);
+	}
+	else {
+
+				/* ========== General case to handle all situations. ========== */
+	
+/*
+ * find amplitude of "Mark" tone.
+ */
+	  m_sum1 = convolve (D->ms_in_cb, D->m_sin_table, D->ms_filter_size);
+	  m_sum2 = convolve (D->ms_in_cb, D->m_cos_table, D->ms_filter_size);
+
+	  m_amp = sqrtf(m_sum1 * m_sum1 + m_sum2 * m_sum2);
+
+/*
+ * Find amplitude of "Space" tone.
+ */
+	  s_sum1 = convolve (D->ms_in_cb, D->s_sin_table, D->ms_filter_size);
+	  s_sum2 = convolve (D->ms_in_cb, D->s_cos_table, D->ms_filter_size);
+
+	  s_amp = sqrtf(s_sum1 * s_sum1 + s_sum2 * s_sum2);
+
+				/* ========== End of general case. ========== */
+	}
+		
+
+/* 
+ * Apply some low pass filtering BEFORE the AGC to remove
+ * overshoot, ringing, and other bad stuff.
+ *
+ * A simple IIR filter is faster but FIR produces better results.
+ *
+ * It is a balancing act between removing high frequency components
+ * from the tone dectection while letting the data thru.
+ */
+
+	if (D->lpf_use_fir) {
+
+	  push_sample (m_amp, D->m_amp_cb, D->lp_filter_size);
+	  m_amp = convolve (D->m_amp_cb, D->lp_filter, D->lp_filter_size);
+
+	  push_sample (s_amp, D->s_amp_cb, D->lp_filter_size);
+	  s_amp = convolve (D->s_amp_cb, D->lp_filter, D->lp_filter_size);
+	}
+	else {
+	
+	  /* Original, but faster, IIR. */
+
+	  m_amp = D->lpf_iir * m_amp + (1.0 - D->lpf_iir) * D->m_amp_prev;
+	  D->m_amp_prev = m_amp;
+
+	  s_amp = D->lpf_iir * s_amp + (1.0 - D->lpf_iir) * D->s_amp_prev;
+	  D->s_amp_prev = s_amp;
+	}
+
+/* 
+ * Which tone is stronger?
+ *
+ * Under real conditions, we find that the higher tone has a
+ * considerably smaller amplitude due to the passband characteristics
+ * of the transmitter and receiver.  To make matters worse, it
+ * varies considerably from one station to another.
+ *
+ * The two filters have different amounts of DC bias.
+ *
+ * Try to compensate for this by normalizing them separately with automatic gain
+ * control (AGC). This works by looking at the minimum and maximum outputs
+ * for each filter and scaling the results to be roughly in the -0.5 to +0.5 range.
+ */
+
+	/* Fast attack and slow decay. */
+	/* Numbers were obtained by trial and error from actual */
+	/* recorded less-than-optimal signals. */
+
+	/* See agc.c and fsk_demod_agc.h for more information. */
+
+	m_norm = agc (m_amp, D->agc_fast_attack, D->agc_slow_decay, &(D->m_peak), &(D->m_valley));
+	s_norm = agc (s_amp, D->agc_fast_attack, D->agc_slow_decay, &(D->s_peak), &(D->s_valley));
+
+	/* Demodulator output is difference between response from two filters. */
+	/* AGC should generally keep this around -1 to +1 range. */
+
+	demod_out = m_norm - s_norm;
+
+/* Try adding some Hysteresis. */
+/* (Not to be confused with Hysteria.) */
+
+	if (demod_out > D->hysteresis) {
+	  demod_data = 1;
+	}
+	else if (demod_out < (- (D->hysteresis))) {
+	  demod_data = 0;
+	} 
+	else {
+	  demod_data = D->prev_demod_data;
+	}
+
+
+/*
+ * Finally, a PLL is used to sample near the centers of the data bits.
+ *
+ * D->data_clock_pll is a SIGNED 32 bit variable.
+ * When it overflows from a large positive value to a negative value, we 
+ * sample a data bit from the demodulated signal.
+ *
+ * Ideally, the the demodulated signal transitions should be near
+ * zero we we sample mid way between the transitions.
+ *
+ * Nudge the PLL by removing some small fraction from the value of 
+ * data_clock_pll, pushing it closer to zero.
+ * 
+ * This adjustment will never change the sign so it won't cause
+ * any erratic data bit sampling.
+ *
+ * If we adjust it too quickly, the clock will have too much jitter.
+ * If we adjust it too slowly, it will take too long to lock on to a new signal.
+ *
+ * Be a little more agressive about adjusting the PLL
+ * phase when searching for a signal.  Don't change it as much when
+ * locked on to a signal.
+ *
+ * I don't think the optimal value will depend on the audio sample rate
+ * because this happens for each transition from the demodulator.
+ */
+	D->prev_d_c_pll = D->data_clock_pll;
+	D->data_clock_pll += D->pll_step_per_sample;
+
+	  //text_color_set(DW_COLOR_DEBUG);
+	  // dw_printf ("prev = %lx, new data clock pll = %lx\n" D->prev_d_c_pll, D->data_clock_pll);
+
+	if (D->data_clock_pll < 0 && D->prev_d_c_pll > 0) {
+
+	  /* Overflow. */
+#if SLICENDICE
+	  hdlc_rec_bit_sam (chan, subchan, demod_data, demod_out);
+#else
+	  hdlc_rec_bit (chan, subchan, demod_data, 0, -1);
+#endif
+	}
+
+        if (demod_data != D->prev_demod_data) {
+
+	  // Note:  Test for this demodulator, not overall for channel.
+
+	  if (hdlc_rec_data_detect_1 (chan, subchan)) {
+	    D->data_clock_pll = (int)(D->data_clock_pll * D->pll_locked_inertia);
+	  }
+	  else {
+	    D->data_clock_pll = (int)(D->data_clock_pll * D->pll_searching_inertia);
+	  }
+	}
+
+
+#if DEBUG4
+
+	if (chan == 0) {
+	if (hdlc_rec_data_detect_1 (chan, subchan)) {
+	  char fname[30];
+
+	  
+	  if (demod_log_fp == NULL) {
+	    seq++;
+	    sprintf (fname, "demod/%04d.csv", seq);
+	    if (seq == 1) mkdir ("demod", 0777);
+
+	    demod_log_fp = fopen (fname, "w");
+	    text_color_set(DW_COLOR_DEBUG);
+	    dw_printf ("Starting demodulator log file %s\n", fname);
+	    fprintf (demod_log_fp, "Audio, Mark, Space, Demod, Data, Clock\n");
+	  }
+	  fprintf (demod_log_fp, "%.3f, %.3f, %.3f, %.3f, %.2f, %.2f\n", fsam + 3.5, m_norm + 2, s_norm + 2, 
+			(m_norm - s_norm) / 2 + 1.5,
+			demod_data ? .9 : .55,  
+			(D->data_clock_pll & 0x80000000) ? .1 : .45);
+	}
+	else {
+	  if (demod_log_fp != NULL) {
+	    fclose (demod_log_fp);
+	    demod_log_fp = NULL;
+	  }
+	}
+	}
+
+#endif
+
+
+/*
+ * Remember demodulator output so we can compare next time.
+ */
+	D->prev_demod_data = demod_data;
+
+
+} /* end demod_afsk_process_sample */
+
+#endif   /* GEN_FFF */
+
+
+#if 0
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        fsk_demod_print_agc
+ *
+ * Purpose:     Print information about input signal amplitude.
+ *		This will be useful for adjusting transmitter audio levels.
+ *		We also want to avoid having an input level so high
+ *		that the A/D converter "clips" the signal.
+ *
+ *
+ * Inputs:	chan	- Audio channel.  0 for left, 1 for right.
+ *
+ * Returns:	None 
+ *
+ * Descripion:	Not sure what to use for final form.
+ *		For now display the AGC peaks for both tones.
+ *		This will be called at the end of a frame.
+ *
+ * Future:	Come up with a sensible scale and add command line option.
+ *		Probably makes more sense to return a single number
+ *		and let the caller print it.
+ *		Just an experiment for now.
+ *
+ *--------------------------------------------------------------------*/
+
+#if 0
+void fsk_demod_print_agc (int chan, int subchan)
+{
+
+	struct demodulator_state_s *D;
+
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+	D = &demodulator_state[chan][subchan];
+
+	dw_printf ("%d\n", (int)((D->lev_last_peak + D->lev_prev_peak)*50));
+
+
+
+	//dw_printf ("Peak= %.2f, %.2f Ave= %.2f, %.2f AGC M= %.2f / %.2f S= %.2f / %.2f\n", 
+	//	D->lev_last_peak, D->lev_prev_peak, D->lev_last_ave, D->lev_prev_ave,
+	//	D->m_peak, D->m_valley, D->s_peak, D->s_valley);
+
+}
+#endif
+
+/* Resulting scale is 0 to almost 100. */
+/* Cranking up the input level produces no more than 97 or 98. */
+/* We currently produce a message when this goes over 90. */
+
+int fsk_demod_get_audio_level (int chan, int subchan) 
+{
+	struct demodulator_state_s *D;
+
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+	D = &demodulator_state[chan][subchan];
+
+	return ( (int) ((D->lev_last_peak + D->lev_prev_peak) * 50 ) );
+}
+
+
+
+
+#endif   /* 0 */
+
+/* end demod_afsk.c */
diff --git a/demod_afsk.h b/demod_afsk.h
new file mode 100755
index 0000000..0969467
--- /dev/null
+++ b/demod_afsk.h
@@ -0,0 +1,8 @@
+
+/* demod_afsk.h */
+
+
+void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
+			int space_freq, char profile, struct demodulator_state_s *D);
+
+void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D);
diff --git a/digipeater.c b/digipeater.c
new file mode 100755
index 0000000..dff36e9
--- /dev/null
+++ b/digipeater.c
@@ -0,0 +1,772 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:	digipeater.c
+ *
+ * Purpose:	Act as an APRS digital repeater.
+ *
+ *
+ * Description:	Decide whether the specified packet should
+ *		be digipeated and make necessary modifications.
+ *
+ *
+ * References:	APRS Protocol Reference, document version 1.0.1
+ *
+ *			http://www.aprs.org/doc/APRS101.PDF
+ *
+ *		APRS SPEC Addendum 1.1
+ *
+ *			http://www.aprs.org/aprs11.html
+ *
+ *		APRS SPEC Addendum 1.2
+ *
+ *			http://www.aprs.org/aprs12.html
+ *
+ *		"The New n-N Paradigm"
+ *
+ *			http://www.aprs.org/fix14439.html
+ *
+ *		Preemptive Digipeating  (new in version 0.8)
+ *
+ *			http://www.aprs.org/aprs12/preemptive-digipeating.txt
+ *		
+ *------------------------------------------------------------------*/
+
+#define DIGIPEATER_C
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+//#include <ctype.h>	/* for isdigit */
+#include "regex.h"
+#include <sys/unistd.h>
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "digipeater.h"
+#include "textcolor.h"
+#include "dedupe.h"
+#include "tq.h"
+
+
+static packet_t digipeat_match (packet_t pp, char *mycall_rec, char *mycall_xmit, 
+				regex_t *uidigi, regex_t *uitrace, int to_chan, enum preempt_e preempt);
+
+/*
+ * Set by digipeater_init and used later.
+ */
+
+
+static struct digi_config_s my_config;
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	digipeater_init
+ * 
+ * Purpose:	Initialize with stuff from configuration file.
+ *
+ * Input:	p_digi_config	- Address of structure with all the
+ *				necessary configuration details.
+ *		
+ * Outputs:	Make local copy for later use.
+ *		
+ * Description:	Called once at application startup time.
+ *
+ *------------------------------------------------------------------------------*/
+
+void digipeater_init (struct digi_config_s *p_digi_config) 
+{
+	memcpy (&my_config, p_digi_config, sizeof(my_config));
+
+	dedupe_init (p_digi_config->dedupe_time);
+}
+
+
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	digipeater
+ * 
+ * Purpose:	Re-transmit packet if it matches the rules.
+ *
+ * Inputs:	chan	- Radio channel where it was received.
+ *		
+ * 		pp	- Packet object.
+ *		
+ * Returns:	None.
+ *		
+ *
+ *------------------------------------------------------------------------------*/
+
+
+
+void digipeater (int from_chan, packet_t pp)
+{
+	int to_chan;
+	packet_t result;
+
+
+	// dw_printf ("digipeater()\n");
+	
+	assert (from_chan >= 0 && from_chan < my_config.num_chans);
+
+
+/*
+ * First pass:  Look at packets being digipeated to same channel.
+ *
+ * We want these to get out quickly.
+ */
+
+	for (to_chan=0; to_chan<my_config.num_chans; to_chan++) {
+	  if (my_config.enabled[from_chan][to_chan]) {
+	    if (to_chan == from_chan) {
+	      result = digipeat_match (pp, my_config.mycall[from_chan], my_config.mycall[to_chan], 
+			&my_config.alias[from_chan][to_chan], &my_config.wide[from_chan][to_chan], 
+			to_chan, my_config.preempt[from_chan][to_chan]);
+	      if (result != NULL) {
+		dedupe_remember (pp, to_chan);
+	        tq_append (to_chan, TQ_PRIO_0_HI, result);
+	      }
+	    }
+	  }
+	}
+
+
+/*
+ * Second pass:  Look at packets being digipeated to different channel.
+ *
+ * These are lower priority
+ */
+
+	for (to_chan=0; to_chan<my_config.num_chans; to_chan++) {
+	  if (my_config.enabled[from_chan][to_chan]) {
+	    if (to_chan != from_chan) {
+	      result = digipeat_match (pp, my_config.mycall[from_chan], my_config.mycall[to_chan], 
+			&my_config.alias[from_chan][to_chan], &my_config.wide[from_chan][to_chan], 
+			to_chan, my_config.preempt[from_chan][to_chan]);
+	      if (result != NULL) {
+		dedupe_remember (pp, to_chan);
+	        tq_append (to_chan, TQ_PRIO_1_LO, result);
+	      }
+	    }
+	  }
+	}
+
+} /* end digipeater */
+
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	digipeat_match
+ * 
+ * Purpose:	A simple digipeater for APRS.
+ *
+ * Input:	pp	- Pointer to a packet object.
+ *	
+ *		mycall_rec	- Call of my station, with optional SSID,
+ *				associated with the radio channel where the 
+ *				packet was received.
+ *
+ *		mycall_rec	- Call of my station, with optional SSID,
+ *				associated with the radio channel where the 
+ *				packet was received.  Could be the same as
+ *				mycall_rec or different.
+ *
+ *		alias	- Compiled pattern for my station aliases or 
+ *				"trapping" (repeating only once).
+ *
+ *		wide	- Compiled pattern for normal WIDEn-n digipeating.
+ *
+ *		to_chan		- Channel number that we are transmitting to.
+ *				  This is needed to maintain a history for 
+ *			 	  removing duplicates during specified time period.
+ *
+		preempt	- Option for "preemptive" digipeating.
+ *		
+ * Returns:	Packet object for transmission or NULL.
+ *
+ * Description:	The packet will be digipeated if the next unused digipeater
+ *		field matches one of the following:
+ *
+ *			- mycall_rec
+ *			- udigi list (only once)
+ *			- wide list (usual wideN-N rules)
+ *
+ *------------------------------------------------------------------------------*/
+
+static char *dest_ssid_path[16] = { 	
+			"",		/* Use VIA path */
+			"WIDE1-1",
+			"WIDE2-2",
+			"WIDE3-3",
+			"WIDE4-4",
+			"WIDE5-5",
+			"WIDE6-6",
+			"WIDE7-7",
+			"WIDE1-1",	/* North */
+			"WIDE1-1",	/* South */
+			"WIDE1-1",	/* East */
+			"WIDE1-1",	/* West */
+			"WIDE2-2",	/* North */
+			"WIDE2-2",	/* South */
+			"WIDE2-2",	/* East */
+			"WIDE2-2"  };	/* West */
+				  
+
+static packet_t digipeat_match (packet_t pp, char *mycall_rec, char *mycall_xmit, 
+				regex_t *alias, regex_t *wide, int to_chan, enum preempt_e preempt)
+{
+	int ssid;
+	int r;
+	char repeater[AX25_MAX_ADDR_LEN];
+	packet_t result = NULL;
+	int err;
+	char err_msg[100];
+
+
+/*
+ * The spec says:
+ *
+ * 	The SSID in the Destination Address field of all packets is coded to specify
+ * 	the APRS digipeater path.
+ * 	If the Destination Address SSID is �0, the packet follows the standard AX.25
+ * 	digipeater (�VIA�) path contained in the Digipeater Addresses field of the
+ * 	AX.25 frame.
+ * 	If the Destination Address SSID is non-zero, the packet follows one of 15
+ * 	generic APRS digipeater paths.
+ * 
+ *
+ * What if this is non-zero but there is also a digipeater path?
+ * I will ignore this if there is an explicit path.
+ *
+ * Note that this modifies the input.  But only once!
+ * Otherwise we don't want to modify the input because this could be called multiple times.
+ */
+
+	if (ax25_get_num_repeaters(pp) == 0 && (ssid = ax25_get_ssid(pp, AX25_DESTINATION)) > 0) {
+	  ax25_set_addr(pp, AX25_REPEATER_1, dest_ssid_path[ssid]);
+	  ax25_set_ssid(pp, AX25_DESTINATION, 0);
+	  /* Continue with general case, below. */
+	}
+
+/* 
+ * Find the first repeater station which doesn't have "has been repeated" set.
+ *
+ * r = index of the address position in the frame.
+ */
+	r = ax25_get_first_not_repeated(pp);
+
+	if (r < AX25_REPEATER_1) {
+	  return NULL;
+	}
+
+	ax25_get_addr_with_ssid(pp, r, repeater);
+	ssid = ax25_get_ssid(pp, r);
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("First unused digipeater is %s, ssid=%d\n", repeater, ssid);
+#endif
+
+
+/*
+ * First check for explicit use of my call.
+ * In this case, we don't check the history so it would be possible
+ * to have a loop (of limited size) if someone constructed the digipeater paths
+ * correctly.
+ */
+	
+	if (strcmp(repeater, mycall_rec) == 0) {
+	  result = ax25_dup (pp);
+	  /* If using multiple radio channels, they */
+	  /* could have different calls. */
+	  ax25_set_addr (result, r, mycall_xmit);	
+	  ax25_set_h (result, r);
+	  return (result);
+	}
+
+/*
+ * Next try to avoid retransmitting redundant information.
+ * Duplicates are detected by comparing only:
+ *	- source
+ *	- destination
+ *	- info part
+ *	- but none of the digipeaters
+ * A history is kept for some amount of time, typically 30 seconds.
+ * For efficiency, only a checksum, rather than the complete fields
+ * might be kept but the result is the same.
+ * Packets transmitted recently will not be transmitted again during
+ * the specified time period.
+ *
+ */
+
+
+	if (dedupe_check(pp, to_chan)) {
+//#if DEBUG
+	  /* Might be useful if people are wondering why */
+	  /* some are not repeated.  Might cause confusion. */
+
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf ("Digipeater: Drop redundant packet.\n");
+//#endif
+	  assert (result == NULL);
+	  return NULL;
+	}
+
+/*
+ * For the alias pattern, we unconditionally digipeat it once.
+ * i.e.  Just replace it with MYCALL don't even look at the ssid.
+ */
+	err = regexec(alias,repeater,0,NULL,0);
+	if (err == 0) {
+	  result = ax25_dup (pp);
+	  ax25_set_addr (result, r, mycall_xmit);	
+	  ax25_set_h (result, r);
+	  return (result);
+	}
+	else if (err != REG_NOMATCH) {
+	  regerror(err, alias, err_msg, sizeof(err_msg));
+	  text_color_set (DW_COLOR_ERROR);
+	  dw_printf ("%s\n", err_msg);
+	}
+
+/* 
+ * If preemptive digipeating is enabled, try matching my call 
+ * and aliases against all remaining unused digipeaters.
+ */
+
+
+	if (preempt != PREEMPT_OFF) {
+	  int r2;
+
+	  for (r2 = r+1; r2 < ax25_get_num_addr(pp); r2++) {
+	    char repeater2[AX25_MAX_ADDR_LEN];
+
+	    ax25_get_addr_with_ssid(pp, r2, repeater2);
+
+	    //text_color_set (DW_COLOR_DEBUG);
+	    //dw_printf ("test match %d %s\n", r2, repeater2);
+
+	    if (strcmp(repeater2, mycall_rec) == 0 ||
+	        regexec(alias,repeater2,0,NULL,0) == 0) {
+
+	      result = ax25_dup (pp);
+	      ax25_set_addr (result, r2, mycall_xmit);	
+	      ax25_set_h (result, r2);
+
+	      switch (preempt) {
+	        case PREEMPT_DROP:	/* remove all prior */
+	          while (r2 > AX25_REPEATER_1) {
+	            ax25_remove_addr (result, r2-1);
+ 		    r2--;
+	          }
+	          break;
+
+	        case PREEMPT_MARK:
+	          r2--;
+	          while (r2 >= AX25_REPEATER_1 && ax25_get_h(result,r2) == 0) {
+	            ax25_set_h (result, r2);
+ 		    r2--;
+	          }
+	          break;
+
+		case PREEMPT_TRACE:	/* remove prior unused */
+	        default:
+	          while (r2 > AX25_REPEATER_1 && ax25_get_h(result,r2-1) == 0) {
+	            ax25_remove_addr (result, r2-1);
+ 		    r2--;
+	          }
+	          break;
+	      }
+
+	      return (result);
+	    }
+ 	  }
+	}
+
+/*
+ * For the wide pattern, we check the ssid and decrement it.
+ */
+
+	err = regexec(wide,repeater,0,NULL,0);
+	if (err == 0) {
+
+/*
+ * If ssid == 1, we simply replace the repeater with my call and
+ *	mark it as being used.
+ *
+ * Otherwise, if ssid in range of 2 to 7, 
+ *	Decrement y and don't mark repeater as being used.
+ * 	Insert own call ahead of this one for tracing if we don't already have the 
+ *	maximum number of repeaters.
+ */
+
+	  if (ssid == 1) {
+	    result = ax25_dup (pp);
+ 	    ax25_set_addr (result, r, mycall_xmit);	
+	    ax25_set_h (result, r);
+	    return (result);
+	  }
+
+	  if (ssid >= 2 && ssid <= 7) {
+	    result = ax25_dup (pp);
+	    ax25_set_ssid(result, r, ssid-1);	// should be at least 1
+
+	    if (ax25_get_num_repeaters(pp) < AX25_MAX_REPEATERS) {
+	      ax25_insert_addr (result, r, mycall_xmit);	
+	      ax25_set_h (result, r);
+	    }
+	    return (result);
+	  }
+	} 
+	else if (err != REG_NOMATCH) {
+	  regerror(err, wide, err_msg, sizeof(err_msg));
+	  text_color_set (DW_COLOR_ERROR);
+	  dw_printf ("%s\n", err_msg);
+	}
+
+
+/*
+ * Don't repeat it if we get here.
+ */
+	assert (result == NULL);
+	return NULL;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Name:	main
+ * 
+ * Purpose:	Standalone test case for this funtionality.
+ *
+ * Usage:	make -f Makefile.<platform> dtest
+ *		./dtest 
+ *
+ *------------------------------------------------------------------------*/
+
+#if TEST
+
+static char mycall[] = "WB2OSZ-9";
+
+static regex_t alias_re;     
+
+static regex_t wide_re;   
+
+static int failed;
+
+static enum preempt_e preempt = PREEMPT_OFF;
+
+
+static void test (char *in, char *out)
+{
+	packet_t pp, result;
+	//int should_repeat;
+	char rec[256];
+	char xmit[256];
+	unsigned char *pinfo;
+	int info_len;
+	unsigned char frame[AX25_MAX_PACKET_LEN];
+	int frame_len;
+
+	dw_printf ("\n");
+
+/*
+ * As an extra test, change text to internal format back to 
+ * text again to make sure it comes out the same.
+ */
+	pp = ax25_from_text (in, 1);
+	assert (pp != NULL);
+
+	ax25_format_addrs (pp, rec);
+	info_len = ax25_get_info (pp, &pinfo);
+	strcat (rec, (char*)pinfo);
+
+	if (strcmp(in, rec) != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Text/internal/text error %s -> %s\n", in, rec);
+	}
+
+/*
+ * Just for more fun, write as the frame format, read it back
+ * again, and make sure it is still the same.
+ */
+
+	frame_len = ax25_pack (pp, frame);
+	ax25_delete (pp);
+
+	pp = ax25_from_frame (frame, frame_len, 50);
+	ax25_format_addrs (pp, rec);
+	info_len = ax25_get_info (pp, &pinfo);
+	strcat (rec, (char*)pinfo);
+
+	if (strcmp(in, rec) != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("internal/frame/internal/text error %s -> %s\n", in, rec);
+	}
+
+/*
+ * On with the digipeater test.
+ */	
+	
+	text_color_set(DW_COLOR_REC);
+	dw_printf ("Rec\t%s\n", rec);
+
+	result = digipeat_match (pp, mycall, mycall, &alias_re, &wide_re, 0, preempt);
+	
+	if (result != NULL) {
+
+	  dedupe_remember (result, 0);
+	  ax25_format_addrs (result, xmit);
+	  info_len = ax25_get_info (result, &pinfo);
+	  strcat (xmit, (char*)pinfo);
+	  ax25_delete (result);
+	}
+	else {
+	  strcpy (xmit, "");
+	}
+
+	text_color_set(DW_COLOR_XMIT);
+	dw_printf ("Xmit\t%s\n", xmit);
+	
+	if (strcmp(xmit, out) == 0) {
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf ("OK\n");
+	}
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Expect\t%s\n", out);
+ 	  failed++;
+	}
+
+	dw_printf ("\n");
+}
+
+int main (int argc, char *argv[])
+{
+	int e;
+	failed = 0;
+	char message[256];
+
+	dedupe_init (4);
+
+/* 
+ * Compile the patterns. 
+ */
+	e = regcomp (&alias_re, "^WIDE[4-7]-[1-7]|CITYD$", REG_EXTENDED|REG_NOSUB);
+	if (e != 0) {
+	  regerror (e, &alias_re, message, sizeof(message));
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("\n%s\n\n", message);
+	  exit (1);
+	}
+
+	e = regcomp (&wide_re, "^WIDE[1-7]-[1-7]$|^TRACE[1-7]-[1-7]$|^MA[1-7]-[1-7]$", REG_EXTENDED|REG_NOSUB);
+	if (e != 0) {
+	  regerror (e, &wide_re, message, sizeof(message));
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("\n%s\n\n", message);
+	  exit (1);
+	}
+
+/*
+ * Let's start with the most basic cases.
+ */
+
+	test (	"W1ABC>TEST01,TRACE3-3:",
+		"W1ABC>TEST01,WB2OSZ-9*,TRACE3-2:");
+
+	test (	"W1ABC>TEST02,WIDE3-3:",
+		"W1ABC>TEST02,WB2OSZ-9*,WIDE3-2:");
+
+	test (	"W1ABC>TEST03,WIDE3-2:",
+		"W1ABC>TEST03,WB2OSZ-9*,WIDE3-1:");
+
+	test (	"W1ABC>TEST04,WIDE3-1:",
+		"W1ABC>TEST04,WB2OSZ-9*:");
+
+/*
+ * Look at edge case of maximum number of digipeaters.
+ */
+	test (	"W1ABC>TEST11,R1,R2,R3,R4,R5,R6*,WIDE3-3:",
+		"W1ABC>TEST11,R1,R2,R3,R4,R5,R6,WB2OSZ-9*,WIDE3-2:");
+
+	test (	"W1ABC>TEST12,R1,R2,R3,R4,R5,R6,R7*,WIDE3-3:",
+		"W1ABC>TEST12,R1,R2,R3,R4,R5,R6,R7*,WIDE3-2:");
+
+	test (	"W1ABC>TEST13,R1,R2,R3,R4,R5,R6,R7*,WIDE3-1:",
+		"W1ABC>TEST13,R1,R2,R3,R4,R5,R6,R7,WB2OSZ-9*:");
+
+/*
+ * "Trap" large values of "N" by repeating only once.
+ */
+	test (	"W1ABC>TEST21,WIDE4-4:",
+		"W1ABC>TEST21,WB2OSZ-9*:");
+
+	test (	"W1ABC>TEST22,WIDE7-7:",
+		"W1ABC>TEST22,WB2OSZ-9*:");
+
+/*
+ * Only values in range of 1 thru 7 are valid.
+ */
+	test (	"W1ABC>TEST31,WIDE0-4:",
+		"");
+
+	test (	"W1ABC>TEST32,WIDE8-4:",
+		"");
+
+	test (	"W1ABC>TEST33,WIDE2:",
+		"");
+
+
+/*
+ * and a few cases actually heard.
+ */
+
+	test (	"WA1ENO>FN42ND,W1MV-1*,WIDE3-2:",
+		"WA1ENO>FN42ND,W1MV-1,WB2OSZ-9*,WIDE3-1:");
+
+	test (	"W1ON-3>BEACON:",
+		"");
+
+	test (	"W1CMD-9>TQ3Y8P,N1RCW-2,W1CLA-1,N8VIM,WIDE2*:",
+		"");
+
+	test (	"W1CLA-1>APX192,W1GLO-1,WIDE2*:",
+		"");
+
+	test (	"AC1U-9>T2TX4S,AC1U,WIDE1,N8VIM*,WIDE2-1:",
+		"AC1U-9>T2TX4S,AC1U,WIDE1,N8VIM,WB2OSZ-9*:");
+
+/*
+ * Someone is still using the old style and will probably be disappointed.
+ */
+
+	test (	"K1CPD-1>T2SR5R,RELAY*,WIDE,WIDE,SGATE,WIDE:",
+		"");
+
+
+/* 
+ * Change destination SSID to normal digipeater if none specified.
+ */
+	test (	"W1ABC>TEST-3:",
+		"W1ABC>TEST,WB2OSZ-9*,WIDE3-2:");
+
+	test (	"W1DEF>TEST-3,WIDE2-2:",
+		"W1DEF>TEST-3,WB2OSZ-9*,WIDE2-1:");
+
+/*
+ * Drop duplicates within specified time interval.
+ * Only the first 1 of 3 should be retransmitted.
+ */
+
+	test (	"W1XYZ>TEST,R1*,WIDE3-2:info1",
+		"W1XYZ>TEST,R1,WB2OSZ-9*,WIDE3-1:info1");
+
+	test (	"W1XYZ>TEST,R2*,WIDE3-2:info1",
+		"");
+
+	test (	"W1XYZ>TEST,R3*,WIDE3-2:info1",
+		"");
+
+/*
+ * Allow same thing after adequate time.
+ */
+	SLEEP_SEC (5);
+
+	test (	"W1XYZ>TEST,R3*,WIDE3-2:info1",
+		"W1XYZ>TEST,R3,WB2OSZ-9*,WIDE3-1:info1");
+
+/*
+ * Although source and destination match, the info field is different.
+ */
+
+	test (	"W1XYZ>TEST,R1*,WIDE3-2:info4",
+		"W1XYZ>TEST,R1,WB2OSZ-9*,WIDE3-1:info4");
+
+	test (	"W1XYZ>TEST,R1*,WIDE3-2:info5",
+		"W1XYZ>TEST,R1,WB2OSZ-9*,WIDE3-1:info5");
+
+	test (	"W1XYZ>TEST,R1*,WIDE3-2:info6",
+		"W1XYZ>TEST,R1,WB2OSZ-9*,WIDE3-1:info6");
+
+/*
+ * New in version 0.8.
+ * "Preemptive" digipeating looks ahead beyond the first unused digipeater.
+ */
+
+	test (	"W1ABC>TEST11,CITYA*,CITYB,CITYC,CITYD,CITYE:off",
+		"");
+
+	preempt = PREEMPT_DROP;
+
+	test (	"W1ABC>TEST11,CITYA*,CITYB,CITYC,CITYD,CITYE:drop",
+		"W1ABC>TEST11,WB2OSZ-9*,CITYE:drop");
+
+	preempt = PREEMPT_MARK;
+
+	test (	"W1ABC>TEST11,CITYA*,CITYB,CITYC,CITYD,CITYE:mark1",
+		"W1ABC>TEST11,CITYA,CITYB,CITYC,WB2OSZ-9*,CITYE:mark1");
+
+	test (	"W1ABC>TEST11,CITYA*,CITYB,CITYC,WB2OSZ-9,CITYE:mark2",
+		"W1ABC>TEST11,CITYA,CITYB,CITYC,WB2OSZ-9*,CITYE:mark2");
+
+	preempt = PREEMPT_TRACE;
+
+	test (	"W1ABC>TEST11,CITYA*,CITYB,CITYC,CITYD,CITYE:trace1",
+		"W1ABC>TEST11,CITYA,WB2OSZ-9*,CITYE:trace1");
+
+	test (	"W1ABC>TEST11,CITYA*,CITYB,CITYC,CITYD:trace2",
+		"W1ABC>TEST11,CITYA,WB2OSZ-9*:trace2");
+
+	test (	"W1ABC>TEST11,CITYB,CITYC,CITYD:trace3",
+		"W1ABC>TEST11,WB2OSZ-9*:trace3");
+
+	test (	"W1ABC>TEST11,CITYA*,CITYW,CITYX,CITYY,CITYZ:nomatch",
+		"");
+
+
+/* 
+ * Did I miss any cases?
+ */
+
+	if (failed == 0) {
+	  dw_printf ("SUCCESS -- All digipeater tests passed.\n");
+	}
+	else {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("ERROR - %d digipeater tests failed.\n", failed);
+	}
+
+	return ( failed != 0 ); 
+
+} /* end main */
+
+#endif  /* if TEST */
+
+/* end digipeater.c */
diff --git a/digipeater.h b/digipeater.h
new file mode 100755
index 0000000..48f4e1c
--- /dev/null
+++ b/digipeater.h
@@ -0,0 +1,62 @@
+
+
+#ifndef DIGIPEATER_H
+#define DIGIPEATER_H 1
+
+#include "regex.h"
+
+#include "direwolf.h"		/* for MAX_CHANS */
+#include "ax25_pad.h"		/* for packet_t */
+
+/*
+ * Information required for digipeating.
+ *
+ * The configuration file reader fills in this information
+ * and it is passed to digipeater_init at application start up time.
+ */
+
+
+struct digi_config_s {
+
+	int	num_chans;
+
+	char mycall[MAX_CHANS][AX25_MAX_ADDR_LEN];	/* Call associated */
+					/* with each of the radio channels.  */
+					/* Could be the same or different. */
+
+	int	dedupe_time;	/* Don't digipeat duplicate packets */
+				/* within this number of seconds. */
+
+#define DEFAULT_DEDUPE 30
+
+/*
+ * Rules for each of the [from_chan][to_chan] combinations.
+ */
+
+	regex_t	alias[MAX_CHANS][MAX_CHANS];
+
+	regex_t	wide[MAX_CHANS][MAX_CHANS];
+
+	int	enabled[MAX_CHANS][MAX_CHANS];
+
+	enum preempt_e { PREEMPT_OFF, PREEMPT_DROP, PREEMPT_MARK, PREEMPT_TRACE } preempt[MAX_CHANS][MAX_CHANS];
+
+};
+
+/*
+ * Call once at application start up time.
+ */
+
+extern void digipeater_init (struct digi_config_s *p_digi_config);
+
+/*
+ * Call this for each packet received.
+ * Suitable packets will be queued for transmission.
+ */
+
+extern void digipeater (int from_chan, packet_t pp);
+
+#endif 
+
+/* end digipeater.h */
+
diff --git a/direwolf.c b/direwolf.c
new file mode 100755
index 0000000..2edf403
--- /dev/null
+++ b/direwolf.c
@@ -0,0 +1,885 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011, 2012, 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      direwolf.c
+ *
+ * Purpose:   	Main program for "Dire Wolf" which includes:
+ *			
+ *			AFSK modem using the "sound card."
+ *			AX.25 encoder/decoder.
+ *			APRS data encoder / decoder.
+ *			APRS digipeater.
+ *			KISS TNC emulator.
+ *			APRStt (touch tone input) gateway
+ *			Internet Gateway (IGate)
+ *		
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <assert.h>
+#include <string.h>
+#include <signal.h>
+
+#if __WIN32__
+#else
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#endif
+
+
+#define DIREWOLF_C 1
+
+#include "direwolf.h"
+#include "version.h"
+#include "audio.h"
+#include "config.h"
+#include "multi_modem.h"
+#include "demod.h"
+#include "hdlc_rec.h"
+#include "hdlc_rec2.h"
+#include "ax25_pad.h"
+#include "decode_aprs.h"
+#include "textcolor.h"
+#include "server.h"
+#include "kiss.h"
+#include "kissnet.h"
+#include "gen_tone.h"
+#include "digipeater.h"
+#include "tq.h"
+#include "xmit.h"
+#include "ptt.h"
+#include "beacon.h"
+#include "ax25_pad.h"
+#include "redecode.h"
+#include "dtmf.h"
+#include "aprs_tt.h"
+#include "tt_user.h"
+#include "igate.h"
+#include "symbols.h"
+#include "dwgps.h"
+
+
+#if __WIN32__
+static BOOL cleanup_win (int);
+#else
+static void cleanup_linux (int);
+#endif
+
+static void usage (char **argv);
+
+#if __SSE__
+
+static void __cpuid(int cpuinfo[4], int infotype){
+    __asm__ __volatile__ (
+        "cpuid":
+        "=a" (cpuinfo[0]),
+        "=b" (cpuinfo[1]),
+        "=c" (cpuinfo[2]),
+        "=d" (cpuinfo[3]) :
+        "a" (infotype)
+    );
+}
+
+#endif
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        main
+ *
+ * Purpose:     Main program for packet radio virtual TNC.
+ *
+ * Inputs:	Command line arguments.
+ *		See usage message for details.
+ *
+ * Outputs:	Decoded information is written to stdout.
+ *
+ *		A socket and pseudo terminal are created for 
+ *		for communication with other applications.
+ *
+ *--------------------------------------------------------------------*/
+
+static 	struct audio_s modem;
+
+static int d_u_opt = 0;			/* "-d u" command line option. */
+
+
+
+int main (int argc, char *argv[])
+{
+	int err;
+	int eof;
+	int j;
+	char config_file[100];
+	int xmit_calibrate_option = 0;
+	int enable_pseudo_terminal = 0;
+	struct digi_config_s digi_config;
+	struct tt_config_s tt_config;
+	struct igate_config_s igate_config;
+	struct misc_config_s misc_config;
+	int r_opt = 0, n_opt = 0, b_opt = 0, B_opt = 0, D_opt = 0;	/* Command line options. */
+	char input_file[80];
+	
+	int t_opt = 1;							/* Text color option. */
+
+
+#if __WIN32__
+
+// Select UTF-8 code page for console output.
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686036(v=vs.85).aspx
+// This is the default I see for windows terminal:  
+// >chcp
+// Active code page: 437
+
+	//Restore on exit? oldcp = GetConsoleOutputCP();
+	SetConsoleOutputCP(CP_UTF8);
+
+#elif __CYGWIN__
+
+/*
+ * Without this, the ISO Latin 1 characters are displayed as gray boxes.
+ */
+	//setenv ("LANG", "C.ISO-8859-1", 1);
+#else
+
+/*
+ * Default on Raspian & Ubuntu Linux is fine.  Don't know about others.
+ *
+ * Should we look at LANG environment variable and issue a warning
+ * if it doesn't look something like  en_US.UTF-8 ?
+ */
+
+#endif
+
+/*
+ * Pre-scan the command line options for the text color option.
+ * We need to set this before any text output.
+ */
+
+	t_opt = 1;		/* 1 = normal, 0 = no text colors. */
+	for (j=1; j<argc-1; j++) {
+	  if (strcmp(argv[j], "-t") == 0) {
+	    t_opt = atoi (argv[j+1]);
+	    //dw_printf ("DEBUG: text color option = %d.\n", t_opt);
+	  }
+	}
+
+	text_color_init(t_opt);
+	text_color_set(DW_COLOR_INFO);
+	//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test 2\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
+	//dw_printf ("Dire Wolf version %d.%d (%s) Development version\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
+
+	// Note "a" for fix with beacon sent to IGate Server.
+
+	dw_printf ("Dire Wolf version %d.%da\n", MAJOR_VERSION, MINOR_VERSION);
+
+
+#if __WIN32__
+	SetConsoleCtrlHandler (cleanup_win, TRUE);
+#else
+	setlinebuf (stdout);
+	signal (SIGINT, cleanup_linux);
+#endif
+
+
+/* 
+ * Starting with version 0.9, the prebuilt Windows version 
+ * requires a minimum of a Pentium 3 or equivalent so we can
+ * use the SSE instructions.
+ * Try to warn anyone using a CPU from the previous
+ * century rather than just dying for no apparent reason.
+ *
+ * Now, where can I find a Pentium 2 or earlier to test this?
+ */
+
+#if __SSE__
+	int cpuinfo[4];
+	__cpuid (cpuinfo, 0);
+	if (cpuinfo[0] >= 1) {
+	  __cpuid (cpuinfo, 1);
+	  //dw_printf ("debug: cpuinfo = %x, %x, %x, %x\n", cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]);
+	  if ( ! ( cpuinfo[3] & (1 << 25))) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("------------------------------------------------------------------\n");
+	    dw_printf ("This version requires a minimum of a Pentium 3 or equivalent.\n");
+	    dw_printf ("If you are seeing this message, you are probably using a computer\n");
+	    dw_printf ("from the previous century.  See comments in Makefile.win for\n");
+	    dw_printf ("information on how you can recompile it for use with your antique.\n");
+	    dw_printf ("------------------------------------------------------------------\n");
+	  }
+	}
+	text_color_set(DW_COLOR_INFO);
+#endif
+
+/*
+ * This has not been very well tested in 64 bit mode.
+ */
+
+#if 0
+	if (sizeof(int) != 4 || sizeof(long) != 4 || sizeof(char *) != 4) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("------------------------------------------------------------------\n");
+	    dw_printf ("This might not work properly when compiled for a 64 bit target.\n");
+	    dw_printf ("It is recommended that you rebuild it with gcc -m32 option.\n");
+	    dw_printf ("------------------------------------------------------------------\n");
+	}
+#endif
+
+/*
+ * Default location of configuration file is current directory.
+ * Can be overridden by -c command line option.
+ * TODO:  Automatically search other places.
+ */
+	
+	strcpy (config_file, "direwolf.conf");
+
+/*
+ * Look at command line options.
+ * So far, the only one is the configuration file location.
+ */
+
+	strcpy (input_file, "");
+	while (1) {
+          int this_option_optind = optind ? optind : 1;
+          int option_index = 0;
+	  int c;
+          static struct option long_options[] = {
+            {"future1", 1, 0, 0},
+            {"future2", 0, 0, 0},
+            {"future3", 1, 0, 'c'},
+            {0, 0, 0, 0}
+          };
+
+	  /* ':' following option character means arg is required. */
+
+          c = getopt_long(argc, argv, "B:D:c:pxr:b:n:d:t:U",
+                        long_options, &option_index);
+          if (c == -1)
+            break;
+
+          switch (c) {
+
+          case 0:				/* possible future use */
+	    text_color_set(DW_COLOR_DEBUG);
+            dw_printf("option %s", long_options[option_index].name);
+            if (optarg) {
+                dw_printf(" with arg %s", optarg);
+            }
+            dw_printf("\n");
+            break;
+
+
+          case 'c':				/* -c for configuration file name */
+
+	    strcpy (config_file, optarg);
+            break;
+
+#if __WIN32__
+#else
+          case 'p':				/* -p enable pseudo terminal */
+		
+	    /* We want this to be off by default because it hangs */
+	    /* eventually when nothing is reading from other side. */
+
+	    enable_pseudo_terminal = 1;
+            break;
+#endif
+
+          case 'B':				/* -B baud rate and modem properties. */
+	 
+	    B_opt = atoi(optarg);
+            if (B_opt < 100 || B_opt > 10000) {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Use a more reasonable data baud rate in range of 100 - 10000.\n");
+              exit (EXIT_FAILURE);
+            }
+            break;
+
+          case 'D':				/* -D decrease AFSK demodulator sample rate */
+	 
+	    D_opt = atoi(optarg);
+            if (D_opt < 1 || D_opt > 8) {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf ("Crazy value of -D. \n");
+              exit (EXIT_FAILURE);
+            }
+            break;
+
+          case 'x':				/* -x for transmit calibration tones. */
+
+	    xmit_calibrate_option = 1;
+            break;
+
+          case 'r':				/* -r audio samples/sec.  e.g. 44100 */
+	 
+	    r_opt = atoi(optarg);
+	    if (r_opt < MIN_SAMPLES_PER_SEC || r_opt > MAX_SAMPLES_PER_SEC) 
+	    {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf("-r option, audio samples/sec, is out of range.\n");
+	      r_opt = 0;
+   	    }
+            break;
+
+          case 'n':				/* -n number of audio channels.  1 or 2. */
+	 
+	    n_opt = atoi(optarg);
+	    if (n_opt < 1 || n_opt > MAX_CHANS) 
+	    {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf("-n option, number of audio channels, is out of range.\n");
+	      n_opt = 0;
+   	    }
+            break;
+
+          case 'b':				/* -b bits per sample.  8 or 16. */
+	 
+	    b_opt = atoi(optarg);
+	    if (b_opt != 8 && b_opt != 16) 
+	    {
+	      text_color_set(DW_COLOR_ERROR);
+              dw_printf("-b option, bits per sample, must be 8 or 16.\n");
+	      b_opt = 0;
+   	    }
+            break;
+
+          case '?':
+
+            /* Unknown option message was already printed. */
+            usage (argv);
+            break;
+
+	  case 'd':				/* Set debug option. */
+	
+	    switch (optarg[0]) {	
+	      case 'a':  server_set_debug(1); break;
+	      case 'k':  kiss_serial_set_debug (1); break;
+	      case 'n':  kiss_net_set_debug (1); break;
+	      case 'u':  d_u_opt = 1; break;
+	      default: break;
+	    }
+	    break;
+	      
+	  case 't':				/* Was handled earlier. */
+	    break;
+
+
+	  case 'U':				/* Print UTF-8 test and exit. */
+
+	    dw_printf ("\n  UTF-8 test string: ma%c%cana %c%c F%c%c%c%ce\n\n", 
+			0xc3, 0xb1,
+			0xc2, 0xb0,
+			0xc3, 0xbc, 0xc3, 0x9f);
+
+	    exit (0);
+	    break;
+
+
+          default:
+
+            /* Should not be here. */
+	    text_color_set(DW_COLOR_DEBUG);
+            dw_printf("?? getopt returned character code 0%o ??\n", c);
+            usage (argv);
+          }
+	}  /* end while(1) for options */
+
+	if (optind < argc) 
+	{
+
+          if (optind < argc - 1) 
+	  {
+	    text_color_set(DW_COLOR_ERROR);
+            dw_printf ("Warning: File(s) beyond the first are ignored.\n");
+          }
+
+	  strcpy (input_file, argv[optind]);
+
+	}
+
+/*
+ * Get all types of configuration settings from configuration file.
+ *
+ * Possibly override some by command line options.
+ */
+
+	symbols_init ();
+
+	config_init (config_file, &modem, &digi_config, &tt_config, &igate_config, &misc_config);
+
+	if (r_opt != 0) {
+	  modem.samples_per_sec = r_opt;
+	}
+	if (n_opt != 0) {
+	  modem.num_channels = n_opt;
+	}
+	if (b_opt != 0) {
+	  modem.bits_per_sample = b_opt;
+	}
+	if (B_opt != 0) {
+	  modem.baud[0] = B_opt;
+
+	  if (modem.baud[0] < 600) {
+            modem.modem_type[0] = AFSK;
+            modem.mark_freq[0] = 1600;
+            modem.space_freq[0] = 1800;
+	    modem.decimate[0] = 3;
+	  }
+	  else if (modem.baud[0] > 2400) {
+            modem.modem_type[0] = SCRAMBLE;
+            modem.mark_freq[0] = 0;
+            modem.space_freq[0] = 0;
+	  }
+	  else {
+            modem.modem_type[0] = AFSK;
+            modem.mark_freq[0] = 1200;
+            modem.space_freq[0] = 2200;
+	  }
+	}
+
+	if (D_opt != 0) {
+		// Don't document.  This will change.
+	    modem.decimate[0] = D_opt;
+	}
+
+	misc_config.enable_kiss_pt = enable_pseudo_terminal;
+
+	if (strlen(input_file) > 0) {
+	  strcpy (modem.adevice_in, input_file);
+	}
+
+/*
+ * Open the audio source 
+ *	- soundcard
+ *	- stdin
+ *	- UDP
+ * Files not supported at this time.
+ * Can always "cat" the file and pipe it into stdin.
+ */
+
+	err = audio_open (&modem);
+	if (err < 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Pointless to continue without audio device.\n");
+	  SLEEP_SEC(5);
+	  exit (1);
+	}
+
+/*
+ * Initialize the AFSK demodulator and HDLC decoder.
+ */
+	multi_modem_init (&modem);
+
+/*
+ * Initialize the touch tone decoder & APRStt gateway.
+ */
+	dtmf_init (modem.samples_per_sec);
+	aprs_tt_init (&tt_config);
+	tt_user_init (&tt_config);
+
+/*
+ * Should there be an option for audio output level?
+ * Note:  This is not the same as a volume control you would see on the screen.
+ * It is the range of the digital sound representation.
+*/
+	gen_tone_init (&modem, 100);
+
+	assert (modem.bits_per_sample == 8 || modem.bits_per_sample == 16);
+	assert (modem.num_channels == 1 || modem.num_channels == 2);
+	assert (modem.samples_per_sec >= MIN_SAMPLES_PER_SEC && modem.samples_per_sec <= MAX_SAMPLES_PER_SEC);
+
+/*
+ * Initialize the transmit queue.
+ */
+
+	xmit_init (&modem);
+
+/*
+ * If -x option specified, transmit alternating tones for transmitter
+ * audio level adjustment, up to 1 minute then quit.
+ * TODO:  enhance for more than one channel.
+ */
+
+	if (xmit_calibrate_option) {
+
+	  int max_duration = 60;  /* seconds */
+	  int n = modem.baud[0] * max_duration;
+	  int chan = 0;
+	
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf ("\nSending transmit calibration tones.  Press control-C to terminate.\n");
+
+	  ptt_set (chan, 1);
+	  while (n-- > 0) {
+
+	    tone_gen_put_bit (chan, n & 1);
+
+	  }
+	  ptt_set (chan, 0);
+	  exit (0);
+	}
+
+/*
+ * Initialize the digipeater and IGate functions.
+ */
+	digipeater_init (&digi_config);
+	igate_init (&igate_config, &digi_config);
+
+/*
+ * Provide the AGW & KISS socket interfaces for use by a client application.
+ */
+	server_init (&misc_config);
+	kissnet_init (&misc_config);
+
+/*
+ * Create a pseudo terminal and KISS TNC emulator.
+ */
+	kiss_init (&misc_config);
+
+/* 
+ * Create thread for trying to salvage frames with bad FCS.
+ */
+	redecode_init ();
+
+/*
+ * Enable beaconing.
+ */
+	beacon_init (&misc_config, &digi_config);
+
+
+/*
+ * Get sound samples and decode them.
+ * Use hot attribute for all functions called for every audio sample.
+ * TODO: separate function with __attribute__((hot))
+ */
+	eof = 0;
+	while ( ! eof) 
+	{
+
+	  int audio_sample;
+	  int c;
+	  char tt;
+
+	  for (c=0; c<modem.num_channels; c++)
+	  {
+	    audio_sample = demod_get_sample ();
+	  
+ 	    if (audio_sample >= 256 * 256) 
+	      eof = 1;
+
+	    multi_modem_process_sample(c,audio_sample);
+
+
+	    /* Previously, the DTMF decoder was always active. */
+	    /* It took very little CPU time and the thinking was that an */
+	    /* attached application might be interested in this even when */
+	    /* the APRStt gateway was not being used.  */
+	    /* Unfortunately it resulted in too many false detections of */
+	    /* touch tones when hearing other types of digital communications */
+	    /* on HF.  Starting in version 1.0, the DTMF decoder is active */
+	    /* only when the APRStt gateway is configured. */
+
+ 	    if (tt_config.obj_xmit_header[0] != '\0') {
+	      tt = dtmf_sample (c, audio_sample/16384.);
+	      if (tt != ' ') {
+	        aprs_tt_button (c, tt);
+	      }
+	    }
+	  }
+
+		/* When a complete frame is accumulated, */
+		/* process_rec_frame, below, is called. */
+
+	}
+
+	exit (EXIT_SUCCESS);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        app_process_rec_frame
+ *
+ * Purpose:     This is called when we receive a frame with a valid 
+ *		FCS and acceptable size.
+ *
+ * Inputs:	chan	- Audio channel number, 0 or 1.
+ *		subchan	- Which modem caught it.  
+ *			  Special case -1 for APRStt gateway.
+ *		pp	- Packet handle.
+ *		alevel	- Audio level, range of 0 - 100.
+ *				(Special case, use negative to skip
+ *				 display of audio level line.
+ *				 Use -2 to indicate DTMF message.)
+ *		retries	- Level of bit correction used.
+ *		spectrum - Display of how well multiple decoders did.
+ *
+ *
+ * Description:	Print decoded packet.
+ *		Optionally send to another application.
+ *
+ *--------------------------------------------------------------------*/
+
+
+void app_process_rec_packet (int chan, int subchan, packet_t pp, int alevel, retry_t retries, char *spectrum)  
+{	
+	
+	char stemp[500];
+	unsigned char *pinfo;
+	int info_len;
+	char heard[AX25_MAX_ADDR_LEN];
+	//int j;
+	int h;
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= -1 && subchan < MAX_SUBCHANS);
+	     
+	  
+	ax25_format_addrs (pp, stemp);
+
+	info_len = ax25_get_info (pp, &pinfo);
+
+	/* Print so we can see what is going on. */
+
+	/* Display audio input level. */
+        /* Who are we hearing?   Original station or digipeater. */
+
+	if (ax25_get_num_addr(pp) == 0) {
+	  /* Not AX.25. No station to display below. */
+	  h = -1;
+	  strcpy (heard, "");
+	}
+	else {
+	  h = ax25_get_heard(pp);
+          ax25_get_addr_with_ssid(pp, h, heard);
+	}
+
+	if (alevel >= 0) {
+
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("\n");
+
+	  if (h != -1 && h != AX25_SOURCE) {
+	    dw_printf ("Digipeater ");
+	  }
+
+	  /* As suggested by KJ4ERJ, if we are receiving from */
+	  /* WIDEn-0, it is quite likely (but not guaranteed), that */
+	  /* we are actually hearing the preceding station in the path. */
+
+	  if (h >= AX25_REPEATER_2 && 
+	      strncmp(heard, "WIDE", 4) == 0 &&
+	      isdigit(heard[4]) &&
+	      heard[5] == '\0') {
+
+	    char probably_really[AX25_MAX_ADDR_LEN];
+
+	    ax25_get_addr_with_ssid(pp, h-1, probably_really);
+	    dw_printf ("%s (probably %s) audio level = %d   [%s]   %s\n", heard, probably_really, alevel, retry_text[(int)retries], spectrum);
+	  }
+	  else {
+	    dw_printf ("%s audio level = %d   [%s]   %s\n", heard, alevel, retry_text[(int)retries], spectrum);
+	  }
+
+	  /* Cranking up the input currently produces */
+	  /* no more than 97.  Issue a warning before we */
+	  /* reach this saturation point. */
+
+	  if (alevel > 90) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Audio input level is too high.  Reduce so most stations are around 50.\n");
+	  }
+	}
+
+// Display non-APRS packets in a different color.
+
+// Display subchannel only when multiple modems configured for channel.
+
+// -1 for APRStt DTMF decoder.
+
+	if (subchan == -1) {
+	  text_color_set(DW_COLOR_REC);
+	  dw_printf ("[%d.dtmf] ", chan);
+	}
+	else {
+	  if (ax25_is_aprs(pp)) {
+	    text_color_set(DW_COLOR_REC);
+	  }
+	  else {
+	    text_color_set(DW_COLOR_DEBUG);
+	  }
+	  if (modem.num_subchan[chan] > 1) {
+	    dw_printf ("[%d.%d] ", chan, subchan);
+	  }
+	  else {
+	    dw_printf ("[%d] ", chan);
+	  }
+	}
+
+	dw_printf ("%s", stemp);			/* stations followed by : */
+	ax25_safe_print ((char *)pinfo, info_len, 0);
+	dw_printf ("\n");
+
+// Display in pure ASCII if non-ASCII characters and "-d u" option specified.
+
+	if (d_u_opt) {
+
+	  unsigned char *p;
+	  int n = 0;
+
+	  for (p = pinfo; *p != '\0'; p++) {
+	    if (*p >= 0x80) n++;
+	  }
+
+	  if (n > 0) {
+	    text_color_set(DW_COLOR_DEBUG);
+	    ax25_safe_print ((char *)pinfo, info_len, 1);
+	    dw_printf ("\n");
+	  }
+	}
+
+/* Decode the contents of APRS frames and display in human-readable form. */
+
+	if (ax25_is_aprs(pp)) {
+	  decode_aprs (pp);
+	}
+
+/* Send to another application if connected. */
+
+	int flen;
+	unsigned char fbuf[AX25_MAX_PACKET_LEN];
+
+	flen = ax25_pack(pp, fbuf);
+
+	server_send_rec_packet (chan, pp, fbuf, flen);
+	kissnet_send_rec_packet (chan, fbuf, flen);
+	kiss_send_rec_packet (chan, fbuf, flen);
+
+/* Send to Internet server if option is enabled. */
+/* Consider only those with correct CRC. */
+
+	if (ax25_is_aprs(pp) && retries == RETRY_NONE) {
+	  igate_send_rec_packet (chan, pp);
+	}
+
+/* Note that packet can be modified in place so this is the last thing we should do with it. */
+/* Again, use only those with correct CRC. */
+/* We don't want to spread corrupted data! */
+/* Single bit change appears to be safe from observations so far but be cautious. */
+
+	if (ax25_is_aprs(pp) && retries == RETRY_NONE) {
+	  digipeater (chan, pp);
+	}
+
+	ax25_delete (pp);
+	
+} /* end app_process_rec_packet */
+
+
+/* Process control C and window close events. */
+
+#if __WIN32__
+
+static BOOL cleanup_win (int ctrltype)
+{
+	if (ctrltype == CTRL_C_EVENT || ctrltype == CTRL_CLOSE_EVENT) {
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf ("\nQRT\n");
+	  ptt_term ();
+	  dwgps_term ();
+	  SLEEP_SEC(1);
+	  ExitProcess (0);
+	}
+	return (TRUE);
+}
+
+
+#else
+
+static void cleanup_linux (int x)
+{
+	text_color_set(DW_COLOR_INFO);
+	dw_printf ("\nQRT\n");
+	ptt_term ();
+	dwgps_term ();
+	exit(0);
+}
+
+#endif
+
+
+
+static void usage (char **argv)
+{
+	text_color_set(DW_COLOR_ERROR);
+
+	dw_printf ("\n");
+	dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
+	dw_printf ("\n");
+	dw_printf ("Usage: direwolf [options]\n");
+	dw_printf ("Options:\n");
+	dw_printf ("    -c fname       Configuration file name.\n");
+
+	dw_printf ("    -r n           Audio sample rate, per sec.\n");
+	dw_printf ("    -n n           Number of audio channels, 1 or 2.\n");
+	dw_printf ("    -b n           Bits per audio sample, 8 or 16.\n");
+	dw_printf ("    -B n           Data rate in bits/sec.  Standard values are 300, 1200, 9600.\n");
+	dw_printf ("                     If < 600, AFSK tones are set to 1600 & 1800.\n");
+	dw_printf ("                     If > 2400, K9NG/G3RUH style encoding is used.\n");
+	dw_printf ("                     Otherwise, AFSK tones are set to 1200 & 2200.\n");
+
+	dw_printf ("    -d             Debug communication with client application, one of\n");
+	dw_printf ("       a             a = AGWPE network protocol.\n");
+	dw_printf ("       k             k = KISS serial port.\n");
+	dw_printf ("       n             n = KISS network.\n");
+	dw_printf ("       u             u = Display non-ASCII text in hexadecimal.\n");
+
+	dw_printf ("    -t n           Text colors.  1=normal, 0=disabled.\n");
+
+#if __WIN32__
+#else
+	dw_printf ("    -p             Enable pseudo terminal for KISS protocol.\n");
+#endif
+	dw_printf ("    -x             Send Xmit level calibration tones.\n");
+	dw_printf ("    -U             Print UTF-8 test string and exit.\n");
+	dw_printf ("\n");
+
+#if __WIN32__
+#else
+	dw_printf ("Complete documentation can be found in /usr/local/share/doc/direwolf.\n");
+#endif
+	exit (EXIT_FAILURE);
+}
+
+
+
+/* end direwolf.c */
diff --git a/direwolf.conf b/direwolf.conf
new file mode 100755
index 0000000..87aa694
--- /dev/null
+++ b/direwolf.conf
@@ -0,0 +1,592 @@
+#############################################################
+#                                                           #
+#               Configuration file for Dire Wolf            #
+#                                                           #
+#############################################################
+#
+# Consult the User Guide for more details on configuration options.
+#
+#
+# These are the most likely settings you might change:
+#
+#	(1)   	MYCALL 	-  call sign and SSID for your station.
+#
+#			Look for lines starting with MYCALL and 
+#			change NOCALL to your own.
+#
+#
+#	(2)	PBEACON	-  enable position beaconing.
+#
+#			Look for lines starting with PBEACON and 
+#			modify for your call, location, etc.
+#
+#
+#	(3)	DIGIPEATER  -  configure digipeating rules.
+#
+#			Look for lines starting with DIGIPEATER.
+#			Most people will probably use the first example.
+#			Just remove the "#" from the start of the line
+#			to enable it.
+#
+#
+#	(4)	IGSERVER, IGLOGIN  - IGate server and login
+#
+#			Configure an IGate client to relay messages between 
+#			radio and internet servers.
+#
+#
+# The default location is "direwolf.conf" in the current working directory.
+# On Linux, the user's home directory will also be searched.
+# An alternate configuration file location can be specified with the "-c" command line option.  
+#
+# As you probably guessed by now, # indicates a comment line.
+#
+# Remove the # at the beginning of a line if you want to use a sample
+# configuration that is currently commented out.
+#
+# Commands are a keyword followed by parameters.
+#
+# Command key words are case insensitive.  i.e. upper and lower case are equivalent.
+#
+# Command parameters are generally case sensitive.  i.e. upper and lower case are different.
+#
+# Example:  The next two are equivalent
+#
+#	PTT /dev/ttyS0 RTS
+#	ptt /dev/ttyS0 RTS
+#
+# But this not equivalent because device names are case sensitive.
+#
+#	PTT /dev/TTYs0 RTS
+#
+
+
+#############################################################
+#                                                           #
+#               AUDIO DEVICE PROPERTIES                     #
+#                                                           #
+#############################################################
+
+#
+# Many people will simply use the default sound device.
+# Some might want to use an alternative device by chosing it here.
+#
+# When the Windows version starts up, it displays something like 
+# this with the available sound devices and capabilities:
+#
+#	Available audio input devices for receive (*=selected):
+#	  0: Microphone (Realtek High Defini
+#	  1: Microphone (Bluetooth SCO Audio
+#	  2: Microphone (Bluetooth AV Audio)
+#	  3: Microphone (USB PnP Sound Devic
+#	Available audio output devices for transmit (*=selected):
+#	  0: Speakers (Realtek High Definiti
+#	  1: Speakers (Bluetooth SCO Audio)
+#	  2: Realtek Digital Output (Realtek
+#	  3: Realtek Digital Output(Optical)
+#	  4: Speakers (Bluetooth AV Audio)
+#	  5: Speakers (USB PnP Sound Device)
+
+# Example: To use the USB Audio, use a command like this with
+# the input and output device numbers.  (Remove the # comment character.)
+
+#ADEVICE  3 5 
+
+# The position in the list can change when devices (e.g. USB) are added and removed.
+# You can also specify devices by using part of the name.
+# Here is an example of specifying the USB Audio device.
+# This is case-sensitive.  Upper and lower case are not treated the same.
+
+#ADEVICE USB
+
+
+# Linux ALSA is complicated.  See User Guide for discussion.
+# To use something other than the default, generally use plughw
+# and a card number reported by "arecord -l" command.  Examples:
+
+# ADEVICE  plughw:CARD=Device,DEV=0
+# ADEVICE  plughw:1,0
+
+# Starting with version 1.0, you can also use "-" or "stdin" to 
+# pipe stdout from some other application such as a software defined
+# radio.  You can also specify "UDP:" and an optional port for input.
+# Something different must be specified for output.
+
+# ADEVICE - plughw:1,0
+# ADEVICE UDP:7355 default
+
+#
+# This is the sound card audio sample rate.
+# The default is 44100.  Other standard values are 22050 or 11025.
+# 
+# Change this only if your computer can't keep up.
+# A lower rate means lower CPU demands but performance will be degraded.
+#
+
+ARATE 44100
+
+
+#
+# Number of audio channels.  1 or 2.
+# If you specify 2, it is possible to attach two different transceivers
+# and receive from both simultaneously.
+#
+
+ACHANNELS 1
+
+# Use this instead if you want to use two transceivers.
+
+#ACHANNELS 2
+
+
+#############################################################
+#                                                           #
+#               CHANNEL 0 PROPERTIES                        #
+#                                                           #
+#############################################################
+
+CHANNEL 0
+
+#
+# The following will apply to the first or only channel.
+# When two channels are used, this is the left audio channel.
+#
+
+
+#
+# Station identifier for this channel.
+# Multiple channels can have the same or different names.
+#
+# It can be up to 6 letters and digits with an optional ssid.
+# The APRS specification requires that it be upper case.
+#
+# Example (don't use this unless you are me):  MYCALL	WB2OSZ-5
+#
+
+MYCALL NOCALL
+
+#
+# VHF FM operation normally uses 1200 baud data with AFSK tones of 1200 and 2200 Hz.
+# 
+
+MODEM 1200 1200 2200 
+
+#
+# 200 Hz shift is normally used for 300 baud HF SSB operation.
+#
+# Note that if you change the tones here, you will need to adjust
+# your tuning dial accordingly to get the same transmitted frequencies.
+#
+# In the second example, we have 7 demodulators spaced 30 Hz apart
+# to capture signals that are off frequency.
+# If you run out of CPU power, drop the audio sample rate down to 22050.
+
+#MODEM 300 1600 1800
+#MODEM 300 1600 1800 7 30
+
+# 
+# 9600 baud doesn't use AFSK so no tones are listed.
+#
+
+#MODEM 9600
+
+
+# 
+# If not using a VOX circuit, the transmitter Push to Talk (PTT) 
+# control is usually wired to a serial port with a suitable interface circuit.  
+# DON'T connect it directly!
+#
+# For the PTT command, specify the device and either RTS or DTR.
+# RTS or DTR may be preceded by "-" to invert the signal.
+#
+
+#PTT COM1 RTS
+#PTT COM1 -DTR
+#PTT /dev/ttyUSB0 RTS
+
+#
+# On Linux, you can also use general purpose I/O pins if
+# your system is configured for user access to them. 
+# This would apply mostly to microprocessor boards, not a regular PC.
+# See separate Raspberry Pi document for more details.
+# The number may be preceded by "-" to invert the signal.
+#
+
+#PTT GPIO 25
+
+
+#
+# After turning on transmitter, send "flag" characters for 
+# TXDELAY * 10 milliseconds for transmitter to stabilize before 
+# sending data.   300 milliseconds is a good default.
+#
+
+TXDELAY 30
+
+#
+# Keep transmitting for TXTAIL * 10 milliseconds after sending
+# the data.  This is needed to avoid dropping PTT too soon and 
+# chopping of the end of the data because we don't have
+# precise control of when the sound will actually get out.
+#
+
+TXTAIL 10
+
+
+#############################################################
+#                                                           #
+#               CHANNEL 1 PROPERTIES                        #
+#                                                           #
+#############################################################
+
+CHANNEL 1
+
+#
+# The following will apply to the second (right) channel if ACHANNELS is 2.
+#
+
+#
+# The two radio channels can have the same or different station identifiers.
+#
+#
+# Example (don't use this unless you are me):  MYCALL	WB2OSZ-5
+#
+
+MYCALL NOCALL
+
+MODEM 1200 1200 2200 
+
+#
+# For this example, we use the same serial port for both
+# transmitters.   RTS for channel 0 and DTR for channel 1.
+#
+
+#PTT COM1 DTR
+
+TXDELAY 30
+TXTAIL 10
+
+
+
+#############################################################
+#                                                           #
+#               VIRTUAL TNC SERVER PROPERTIES               #
+#                                                           #
+#############################################################
+
+#
+# Dire Wolf acts as a virtual TNC and can communicate with
+# two different protocols:
+#	- the �AGW TCPIP Socket Interface� - default port 8000
+#	- KISS TNC via serial port
+#	- KISS protocol over TCP socket - default port 8001
+#
+# See descriptions of AGWPORT, KISSPORT, and NULLMODEM in the
+# User Guide for more details.
+#
+
+AGWPORT 8000
+KISSPORT 8001
+
+#
+# Some applications are designed to operate with only a physical
+# TNC attached to a serial port.  For these, we provide a virtual serial
+# port ("pseudo terminal" in Linux) that appears to be connected to a TNC.
+#
+# Linux:
+# 	Linux applications can often specify "/tmp/kisstnc"
+# 	for the serial port name.  Behind the scenes, Dire Wolf 
+# 	creates a pseudo terminal.  Unfortunately we can't specify the name
+# 	and we wouldn't want to reconfigure the application each time.
+# 	To get around this, /tmp/kisstnc is a symbolic link to the
+# 	non-constant pseudo terminal name.
+#
+# 	Use the -p command line option to enable this feature.
+#
+# Windows:
+# 
+#  	Microsoft Windows applications need a serial port
+# 	name like COM1, COM2, COM3, or COM4.
+#
+# 	Take a look at the User Guide for instructions to set up
+# 	two virtual serial ports named COM3 and COM4 connected by
+# 	a null modem.
+#
+# 	Using the default configuration, Dire Wolf will connect to 
+# 	COM3 and the client application will use COM4.
+#
+# 	Uncomment following line to use this feature.
+
+#NULLMODEM COM3
+
+
+#
+# Version 0.6 adds a new feature where it is sometimes possible
+# to recover frames with a bad FCS.  Several levels of effort
+# are possible.  
+#
+#	0 [NONE] - Don't try to repair.
+#	1 [SINGLE] - Attempt to fix single bit error.  (default)
+#	2 [DOUBLE] - Also attempt to fix two adjacent bits.
+#	3 [TRIPLE] - Also attempt to fix three adjacent bits.
+#	4 [TWO_SEP] - Also attempt to fix two non-adjacent (separated) bits.
+#
+
+FIX_BITS 1
+
+#	
+#############################################################
+#                                                           #
+#               BEACONING PROPERTIES                        #
+#                                                           #
+#############################################################
+
+
+#
+# Beaconing is configured with these two commands:
+#
+#	PBEACON		- for a position report (usually yourself)
+#	OBEACON		- for an object report (usually some other entity)
+#
+# Each has a series of keywords and values for options.  
+# See User Guide for details.
+#
+# Example:
+#
+# This results in a broadcast once every 10 minutes.
+# Every half hour, it can travel via two digipeater hops.
+# The others are kept local.
+#
+
+#PBEACON delay=00:15 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA" via=WIDE1-1,WIDE2-1 
+#PBEACON delay=10:15 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA"  
+#PBEACON delay=20:15 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA"  
+
+#
+# Modify this for your particular situation before removing 
+# the # comment character from the beginning of the lines above.
+# 
+
+
+#############################################################
+#                                                           #
+#               DIGIPEATER PROPERTIES                       #
+#                                                           #
+#############################################################
+
+#
+# Digipeating is activated with commands of the form:
+#
+#	DIGIPEAT from-chan to-chan aliases wide [ preemptive ]
+#
+# where,
+#
+#	from-chan	is the channel where the packet is received.
+#
+#	to-chan		is the channel where the packet is to be re-transmitted.
+#
+#	aliases		is a pattern for digipeating ONCE.  Anything matching
+#			this pattern is effectively treated like WIDE1-1.
+#			'MYCALL' for the receiving channel is an implied
+#			member of this list.
+#
+#	wide		is the pattern for normal WIDEn-N digipeating
+#			where the ssid is decremented. 
+#
+#	preemptive	is the "preemptive" digipeating option.  See
+#			User Guide for more details.
+#
+# Pattern matching uses "extended regular expressions."  Rather than listing 
+# all the different possibilities (such as "WIDE3-3,WIDE4-4,WIDE5-5,WIDE6-6,WIDE7-7"),
+# a pattern can be specified such as "^WIDE[34567]-[1-7]$".  This means:
+#
+#	^	beginning of call.  Without this, leading characters 
+#		don't need to match and ZWIDE3-3 would end up matching.
+#
+#	WIDE	is an exact literal match of upper case letters W I D E.
+#
+#	[34567]	means ANY ONE of the characters listed.
+#
+#	-	is an exact literal match of the "-" character (when not
+#		found inside of []).
+#
+# 	[1-7]	is an alternative form where we have a range of characters
+#		rather than listing them all individually.
+#
+#	$	means end of call.  Without this, trailing characters don't
+#		need to match.  As an example, we would end up matching 
+#		WIDE3-15 besides WIDE3-1.
+#
+# Google "Extended Regular Expressions" for more information.
+#
+
+#
+# If the first unused digipeater field, in the received packet,
+# matches the first pattern, it is treated the same way you
+# would expect WIDE1-1 to behave.
+#
+# The digipeater name is replaced by MYCALL of the destination channel.
+#
+# Example:	W1ABC>APRS,WIDE7-7
+# Becomes:	W1ABC>APRS,WB2OSZ-5*
+#
+# In this example, we trap large values of N as recommended in 
+# http://www.aprs.org/fix14439.html
+#
+
+#
+# If not caught by the first pattern, see if it matches the second pattern.
+#
+# Matches will be processed with the usual WIDEn-N rules.
+# 
+# If N >= 2, the N value is decremented and MYCALL (of the destination
+# 		channel) is inserted if enough room.
+#
+# Example:	W1ABC>APRS,WIDE2-2
+# Becomes:	W1ABC>APRS,WB2OSZ-5*,WIDE2-1
+#
+# If N = 1, we don't want to keep WIDEn-0 in the digipeater list so
+#		the station is replaced by MYCALL.
+#
+# Example:	W1ABC>APRS,W9XYZ*,WIDE2-1
+# Becomes:	W1ABC>APRS,W9XYZ,WB2OSZ-5*
+#
+
+#-------------------------------------------------------
+# ----------  Example 1:  Typical digipeater  ----------
+#-------------------------------------------------------
+
+#
+# For most common situations, use something like this by removing
+# the "#" from the beginning of the line.
+# To disable digipeating, put # at the beginning of the line.
+# 
+
+# DIGIPEAT 0 0 ^WIDE[3-7]-[1-7]$|^TEST$ ^WIDE[12]-[12]$ TRACE
+
+
+
+
+#############################################################
+#                                                           #
+#               INTERNET GATEWAY                            #
+#                                                           #
+#############################################################
+
+# First you need to specify the name of a Tier 2 server.  
+# The current preferred way is to use one of these regional rotate addresses:
+
+#	noam.aprs2.net 		- for North America
+#	soam.aprs2.net		- for South America
+#	euro.aprs2.net		- for Europe and Africa
+#	asia.aprs2.net 		- for Asia
+#	aunz.aprs2.net		- for Oceania 
+
+#IGSERVER noam.aprs2.net
+
+# You also need to specify your login name and passcode. 
+# Contact the author if you can't figure out how to generate the passcode.
+ 
+#IGLOGIN WB2OSZ-5 123456
+
+# That's all you need for a receive only IGate which relays
+# messages from the local radio channel to the global servers.
+
+# Some might want to send an IGate client position directly to a server
+# without sending it over the air and relying on someone else to 
+# forward it to an IGate server.  This is done by using sendto=IG rather
+# than a radio channel number. Overlay R for receive only, T for two way.
+
+#PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=R lat=42^37.14N long=071^20.83W 
+#PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=T lat=42^37.14N long=071^20.83W 
+
+
+# To relay messages from the Internet to radio, you need to add
+# one more option with the transmit channel number and a VIA path.
+
+#IGTXVIA 0 WIDE1-1
+
+# You might want to apply a filter for what packets will be obtained from the server.
+# Read about filters here:  http://www.aprs2.net/wiki/pmwiki.php/Main/FilterGuide
+# Example:
+
+#IGFILTER m/50 
+
+# Finally, we don�t want to flood the radio channel.  
+# The IGate function will limit the number of packets transmitted 
+# during 1 minute and 5 minute intervals.   If a limit would 
+# be exceeded, the packet is dropped and message is displayed in red.
+
+IGTXLIMIT 6 10
+
+
+#############################################################
+#                                                           #
+#               APRStt GATEWAY                              #
+#                                                           #
+#############################################################
+
+#
+# Dire Wolf can receive DTMF (commonly known as Touch Tone)
+# messages and convert them to packet objects.
+#
+# See "APRStt-Implementation-Notes" document for details.
+#
+
+#
+# Sample gateway configuration based on:
+#
+#	http://www.aprs.org/aprstt/aprstt-coding24.txt
+#	http://www.aprs.org/aprs-jamboree-2013.html
+#
+
+# Define specific points.
+
+TTPOINT  B01  37^55.37N  81^7.86W  			
+TTPOINT  B7495088  42.605237  -71.34456		
+TTPOINT  B934  42.605237  -71.34456			
+
+TTPOINT B901  42.661279  -71.364452 
+TTPOINT B902  42.660411  -71.364419 
+TTPOINT B903  42.659046  -71.364452 
+TTPOINT B904  42.657578  -71.364602 
+
+
+# For location at given bearing and distance from starting point.
+
+TTVECTOR  B5bbbddd  37^55.37N  81^7.86W  0.01  mi
+
+# For location specified by x, y coordinates.
+
+TTGRID   Byyyxxx    37^50.00N  81^00.00W  37^59.99N  81^09.99W   
+
+# UTM location for Lowell-Dracut-Tyngsborough State Forest.
+
+TTUTM  B6xxxyyy  19T  10  300000  4720000
+
+
+
+# Location for the corral.
+
+TTCORRAL   37^55.50N  81^7.00W  0^0.02N
+
+# Compact messages - Fixed locations xx and object yyy where 
+#   	Object numbers 100 � 199	= bicycle	
+#	Object numbers 200 � 299	= fire truck
+#	Others				= dog
+
+TTMACRO  xx1yy  B9xx*AB166*AA2B4C5B3B0A1yy
+TTMACRO  xx2yy  B9xx*AB170*AA3C4C7C3B0A2yy
+TTMACRO  xxyyy  B9xx*AB180*AA3A6C4A0Ayyy
+
+TTMACRO  z  Cz
+
+# Transmit object reports on channel 0 with this header.
+
+#TTOBJ 0 WB2OSZ-5>APDW10
+
+# Advertise gateway position with beacon.
+
+# OBEACON DELAY=0:15 EVERY=10:00 VIA=WIDE1-1 OBJNAME=WB2OSZ-tt SYMBOL=APRStt LAT=42^37.14N LONG=71^20.83W COMMENT="APRStt Gateway"  
+
+
diff --git a/direwolf.desktop b/direwolf.desktop
new file mode 100755
index 0000000..218874f
--- /dev/null
+++ b/direwolf.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Type=Application
+Exec=lxterminal -t "Dire Wolf" -e "/usr/local/bin/direwolf"
+Name=Dire Wolf
+Comment=APRS Soundcard TNC
+Icon=/usr/share/direwolf/dw-icon.png
+Path=/home/pi
+#Terminal=true
+Categories=HamRadio
+Keywords=Ham Radio;APRS;Soundcard TNC;KISS;AGWPE;AX.25
\ No newline at end of file
diff --git a/direwolf.h b/direwolf.h
new file mode 100755
index 0000000..e78c3a5
--- /dev/null
+++ b/direwolf.h
@@ -0,0 +1,39 @@
+
+#ifndef DIREWOLF_H
+#define DIREWOLF_H 1
+
+
+/*
+ * Maximum number of radio channels.
+ */
+
+#define MAX_CHANS 2
+
+/*
+ * Maximum number of modems per channel.
+ * I called them "subchannels" (in the code) because 
+ * it is short and unambiguous.
+ * Nothing magic about the number.  Could be larger
+ * but CPU demands might be overwhelming.
+ */
+
+#define MAX_SUBCHANS 9
+
+
+#if __WIN32__
+#include <windows.h>
+#define SLEEP_SEC(n) Sleep((n)*1000)
+#define SLEEP_MS(n) Sleep(n)
+#else
+#define SLEEP_SEC(n) sleep(n)
+#define SLEEP_MS(n) usleep((n)*1000)
+#endif
+
+#endif
+
+#if __WIN32__
+#define PTW32_STATIC_LIB
+#include "pthreads/pthread.h"
+#else
+#include <pthread.h>
+#endif
\ No newline at end of file
diff --git a/dsp.c b/dsp.c
new file mode 100755
index 0000000..9f58726
--- /dev/null
+++ b/dsp.c
@@ -0,0 +1,248 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        dsp.c
+ *
+ * Purpose:     Generate the filters used by the demodulators.
+ *
+ *----------------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "direwolf.h"
+#include "audio.h"
+#include "fsk_demod_state.h"
+#include "fsk_gen_filter.h"
+#include "textcolor.h"
+#include "dsp.h"
+
+
+//#include "fsk_demod_agc.h"	/* for M_FILTER_SIZE, etc. */
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+
+// Don't remove this.  It serves as a reminder that an experiment is underway.
+
+#if defined(TUNE_MS_FILTER_SIZE) || defined(TUNE_AGC_FAST) || defined(TUNE_LPF_BAUD) || defined(TUNE_PLL_LOCKED) || defined(TUNE_PROFILE)
+#define DEBUG1 1
+#endif
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        window
+ *
+ * Purpose:     Filter window shape functions.
+ *
+ * Inputs:   	type	- BP_WINDOW_HAMMING, etc.
+ *		size	- Number of filter taps.
+ *		j	- Index in range of 0 to size-1.
+ *
+ * Returns:     Multiplier for the window shape.
+ *		
+ *----------------------------------------------------------------*/
+
+float window (bp_window_t type, int size, int j)
+{
+	float center;
+	float w;
+
+	center = 0.5 * (size - 1);
+
+	switch (type) {
+
+	  case BP_WINDOW_COSINE:
+	    w = cos((j - center) / size * M_PI);
+	    //w = sin(j * M_PI / (size - 1));
+	    break;
+
+	  case BP_WINDOW_HAMMING:
+	    w = 0.53836 - 0.46164 * cos((j * 2 * M_PI) / (size - 1));
+	    break;
+
+	  case BP_WINDOW_BLACKMAN:
+	    w =  0.42659 - 0.49656 * cos((j * 2 * M_PI) / (size - 1)) 
+			 + 0.076849 * cos((j * 4 * M_PI) / (size - 1));
+	    break;
+
+	  case BP_WINDOW_FLATTOP:
+	    w =  1.0    - 1.93  * cos((j * 2 * M_PI) / (size - 1)) 
+			+ 1.29  * cos((j * 4 * M_PI) / (size - 1))
+			- 0.388 * cos((j * 6 * M_PI) / (size - 1))
+			+ 0.028 * cos((j * 8 * M_PI) / (size - 1));
+	    break;
+
+	  case BP_WINDOW_TRUNCATED:
+	    default:
+	    w = 1.0;
+	    break;
+	}
+	return (w);
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        gen_lowpass
+ *
+ * Purpose:     Generate low pass filter kernel.
+ *
+ * Inputs:   	fc		- Cutoff frequency as fraction of sampling frequency.
+ *		filter_size	- Number of filter taps.
+ *		wtype		- Window type, BP_WINDOW_HAMMING, etc.
+ *
+ * Outputs:     lp_filter
+ *		
+ *----------------------------------------------------------------*/
+
+ 
+void gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype)
+{
+	int j;
+	float lp_sum;
+
+
+#if DEBUG1
+	text_color_set(DW_COLOR_DEBUG);
+
+	dw_printf ("Lowpass, size=%d, fc=%.2f\n", filter_size, fc);
+	dw_printf ("   j     shape   sinc   final\n");
+#endif
+
+	assert (filter_size >= 3 && filter_size <= MAX_FILTER_SIZE);
+
+        for (j=0; j<filter_size; j++) {
+	  float center;
+	  float sinc;
+	  float shape;
+
+	  center = 0.5 * (filter_size - 1);
+
+	  if (j - center == 0) {
+	    sinc = 2 * fc;
+	  }
+	  else {
+	    sinc = sin(2 * M_PI * fc * (j-center)) / (M_PI*(j-center));
+	  }
+
+	  shape = window (wtype, filter_size, j);
+	  lp_filter[j] = sinc * shape;
+
+#if DEBUG1
+	  dw_printf ("%6d  %6.2f  %6.3f  %6.3f\n", j, shape, sinc, lp_filter[j] ) ;
+#endif
+        }
+
+/*
+ * Normalize lowpass for unity gain.
+ */
+	lp_sum = 0;
+        for (j=0; j<filter_size; j++) {
+	  lp_sum += lp_filter[j];
+	}
+        for (j=0; j<filter_size; j++) {
+	  lp_filter[j] = lp_filter[j] / lp_sum;
+	}
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        gen_bandpass
+ *
+ * Purpose:     Generate band pass filter kernel.
+ *
+ * Inputs:   	f1		- Lower cutoff frequency as fraction of sampling frequency.
+ *		f2		- Upper cutoff frequency...
+ *		filter_size	- Number of filter taps.
+ *		wtype		- Window type, BP_WINDOW_HAMMING, etc.
+ *
+ * Outputs:     bp_filter
+ *
+ * Reference:	http://www.labbookpages.co.uk/audio/firWindowing.html	
+ *
+ *		Does it need to be an odd length?
+ *	
+ *----------------------------------------------------------------*/
+
+
+void gen_bandpass (float f1, float f2, float *bp_filter, int filter_size, bp_window_t wtype)
+{
+	int j;
+	float bp_sum;
+
+
+#if DEBUG1
+	text_color_set(DW_COLOR_DEBUG);
+
+	dw_printf ("Bandpass, size=%d\n", filter_size);
+	dw_printf ("   j     shape   sinc   final\n");
+#endif
+
+	assert (filter_size >= 3 && filter_size <= MAX_FILTER_SIZE);
+
+        for (j=0; j<filter_size; j++) {
+	  float center;
+	  float sinc;
+	  float shape;
+
+	  center = 0.5 * (filter_size - 1);
+
+	  if (j - center == 0) {
+	    sinc = 2 * (f2 - f1);
+	  }
+	  else {
+	    sinc = sin(2 * M_PI * f2 * (j-center)) / (M_PI*(j-center))
+		 - sin(2 * M_PI * f1 * (j-center)) / (M_PI*(j-center));
+	  }
+
+	  shape = window (wtype, filter_size, j);
+	  bp_filter[j] = sinc * shape;
+
+#if DEBUG1
+	  dw_printf ("%6d  %6.2f  %6.3f  %6.3f\n", j, shape, sinc, bp_filter[j] ) ;
+#endif
+        }
+
+/*
+ * Normalize bandpass for unity gain.
+ */
+	bp_sum = 0;
+        for (j=0; j<filter_size; j++) {
+	  bp_sum += bp_filter[j];
+	}
+        for (j=0; j<filter_size; j++) {
+	  bp_filter[j] = bp_filter[j] / bp_sum;
+	}
+}
+
+/* end dsp.c */
\ No newline at end of file
diff --git a/dsp.h b/dsp.h
new file mode 100755
index 0000000..1150714
--- /dev/null
+++ b/dsp.h
@@ -0,0 +1,10 @@
+
+/* dsp.h */
+
+// TODO:  put prefixes on these names.
+
+float window (bp_window_t type, int size, int j);
+
+void gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype);
+
+void gen_bandpass (float f1, float f2, float *bp_filter, int filter_size, bp_window_t wtype);
\ No newline at end of file
diff --git a/dtmf.c b/dtmf.c
new file mode 100755
index 0000000..70dc60b
--- /dev/null
+++ b/dtmf.c
@@ -0,0 +1,411 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+/*------------------------------------------------------------------
+ *
+ * Module:      dtmf.c
+ *
+ * Purpose:   	Decoder for DTMF, commonly known as "touch tones."
+ *		
+ * Description: This uses the Goertzel Algorithm for tone detection.
+ *
+ * References:	http://eetimes.com/design/embedded/4024443/The-Goertzel-Algorithm
+ * 		http://www.ti.com/ww/cn/uprogram/share/ppt/c5000/17dtmf_v13.ppt
+ *
+ *---------------------------------------------------------------*/
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+
+#include "direwolf.h"
+#include "dtmf.h"
+
+
+// Define for unit test.
+//#define DTMF_TEST 1
+
+
+#if DTMF_TEST
+#define TIMEOUT_SEC 1	/* short for unit test below. */
+#define DEBUG 1
+#else
+#define TIMEOUT_SEC 5	/* for normal operation. */
+#endif
+
+
+#define NUM_TONES 8
+static int const dtmf_tones[NUM_TONES] = { 697, 770, 852, 941, 1209, 1336, 1477, 1633 };
+
+/*
+ * Current state of the decoding. 
+ */
+
+static struct {
+	int sample_rate;	/* Samples per sec.  Typ. 44100, 8000, etc. */
+	int block_size;		/* Number of samples to process in one block. */
+	float coef[NUM_TONES];	
+
+	struct {		/* Separate for each audio channel. */
+
+		int n;		/* Samples processed in this block. */
+		float Q1[NUM_TONES];
+		float Q2[NUM_TONES];
+		char prev_dec;	
+		char debounced;
+		char prev_debounced;
+		int timeout;
+	} C[MAX_CHANS];		
+} D;
+
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        dtmf_init
+ *
+ * Purpose:     Initialize the DTMF decoder.
+ *		This should be called once at application start up time.
+ *
+ * Inputs:      sample_rate	- Audio sample frequency, typically 
+ *				  44100, 22050, 8000, etc.
+ *
+ * Returns:     None.
+ *
+ *----------------------------------------------------------------*/
+
+void dtmf_init (int sample_rate)
+{
+	int j;		/* Loop over all tones frequencies. */
+	int c;		/* Loop over all audio channels. */
+
+/*
+ * Processing block size.
+ * Larger = narrower bandwidth, slower response.
+ */
+	D.sample_rate = sample_rate;
+	D.block_size = (205 * sample_rate) / 8000;
+
+#if DEBUG
+	dw_printf ("    freq      k     coef    \n");
+#endif
+	for (j=0; j<NUM_TONES; j++) {
+	  float k; 
+
+
+// Why do some insist on rounding k to the nearest integer?
+// That would move the filter center frequency away from ideal.
+// What is to be gained?
+// More consistent results for all the tones when k is not rounded off.
+
+	  k = D.block_size * (float)(dtmf_tones[j]) / (float)(D.sample_rate);
+
+	  D.coef[j] = 2 * cos(2 * M_PI * (float)k / (float)(D.block_size));
+
+	  assert (D.coef[j] > 0 && D.coef[j] < 2.0);
+#if DEBUG
+	  dw_printf ("%8d   %5.1f   %8.5f  \n", dtmf_tones[j], k, D.coef[j]);
+#endif
+	}
+
+	for (c=0; c<MAX_CHANS; c++) {
+	  D.C[c].n = 0;
+	  for (j=0; j<NUM_TONES; j++) {
+	    D.C[c].Q1[j] = 0;
+	    D.C[c].Q2[j] = 0;
+	  }
+	  D.C[c].prev_dec = ' ';
+	  D.C[c].debounced = ' ';
+	  D.C[c].prev_debounced = ' ';
+	  D.C[c].timeout = 0;
+	}
+
+}
+
+/*------------------------------------------------------------------
+ *
+ * Name:        dtmf_sample
+ *
+ * Purpose:     Process one audio sample from the sound input source.
+ *
+ * Inputs:	c	- Audio channel number.
+ *			  This can process multiple channels in parallel.
+ *		input	- Audio sample.
+ *
+ * Returns:     0123456789ABCD*# for a button push.
+ *		. for nothing happening during sample interval.
+ *		$ after several seconds of inactivity.
+ *		space between sample intervals.
+ *		
+ *
+ *----------------------------------------------------------------*/
+				
+__attribute__((hot))
+char dtmf_sample (int c, float input)
+{
+	int i;
+	float Q0;
+	float output[NUM_TONES];
+	char decoded;
+	char ret;
+	static const char rc2char[16] = { 	'1', '2', '3', 'A',
+						'4', '5', '6', 'B',
+						'7', '8', '9', 'C',
+						'*', '0', '#', 'D' };
+	for (i=0; i<NUM_TONES; i++) {
+	  Q0 = input + D.C[c].Q1[i] * D.coef[i] - D.C[c].Q2[i];
+	  D.C[c].Q2[i] = D.C[c].Q1[i];
+	  D.C[c].Q1[i] = Q0;
+	}
+
+/*
+ * Is it time to process the block?
+ */
+	D.C[c].n++;
+	if (D.C[c].n == D.block_size) {
+	  int row, col;
+
+	  for (i=0; i<NUM_TONES; i++) {
+	    output[i] = sqrt(D.C[c].Q1[i] * D.C[c].Q1[i] + D.C[c].Q2[i] * D.C[c].Q2[i] - D.C[c].Q1[i] * D.C[c].Q2[i] * D.coef[i]);
+	    D.C[c].Q1[i] = 0;
+	    D.C[c].Q2[i] = 0;
+	  }
+	  D.C[c].n = 0;
+
+
+/*
+ * The input signal can vary over a couple orders of
+ * magnitude so we can't set some absolute threshold.
+ *
+ * See if one tone is stronger than the sum of the 
+ * others in the same group multiplied by some factor.
+ *
+ * For perfect synthetic signals this needs to be in
+ * the range of about 1.33 (very senstive) to 2.15 (very fussy).
+ *
+ * Too low will cause false triggers on random noise.
+ * Too high will won't decode less than perfect signals.
+ *
+ * Use the mid point 1.74 as our initial guess.
+ * It might need some fine tuning for imperfect real world signals.
+ */
+
+
+#define THRESHOLD 1.74
+
+	  if      (output[0] > THRESHOLD * (            output[1] + output[2] + output[3])) row = 0;
+	  else if (output[1] > THRESHOLD * (output[0]             + output[2] + output[3])) row = 1;
+	  else if (output[2] > THRESHOLD * (output[0] + output[1]             + output[3])) row = 2;
+	  else if (output[3] > THRESHOLD * (output[0] + output[1] + output[2]            )) row = 3;
+	  else row = -1;
+
+	  if      (output[4] > THRESHOLD * (            output[5] + output[6] + output[7])) col = 0;
+	  else if (output[5] > THRESHOLD * (output[4]             + output[6] + output[7])) col = 1;
+	  else if (output[6] > THRESHOLD * (output[4] + output[5]             + output[7])) col = 2;
+	  else if (output[7] > THRESHOLD * (output[4] + output[5] + output[6]            )) col = 3;
+	  else col = -1;
+
+	  for (i=0; i<NUM_TONES; i++) {
+#if DEBUG
+	    dw_printf ("%5.0f ", output[i]);
+#endif
+	  }
+	  if (row >= 0 && col >= 0) {
+	    decoded = rc2char[row*4+col];
+	  }
+	  else {
+	    decoded = '.';
+	  }
+
+// Consider valid only if we get same twice in a row.
+
+	  if (decoded == D.C[c].prev_dec) {
+	    D.C[c].debounced = decoded;
+	    /* Reset timeout timer. */
+	    if (decoded != ' ') {
+	      D.C[c].timeout = ((TIMEOUT_SEC) * D.sample_rate) / D.block_size;
+	    }
+	  }
+	  D.C[c].prev_dec = decoded;
+
+// Return only new button pushes.
+// Also report timeout after period of inactivity.
+
+	  ret = '.';
+	  if (D.C[c].debounced != D.C[c].prev_debounced) {
+	    if (D.C[c].debounced != ' ') {
+	      ret = D.C[c].debounced;
+	    }
+	  }
+	  if (ret == '.') {
+	    if (D.C[c].timeout > 0) {
+	      D.C[c].timeout--;
+	      if (D.C[c].timeout == 0) {
+	        ret = '$';
+              }
+            }
+	  }
+	  D.C[c].prev_debounced = D.C[c].debounced;
+
+#if DEBUG
+	  dw_printf ("     dec=%c, deb=%c, ret=%c \n", 
+			decoded, D.C[c].debounced, ret);
+#endif
+	  return (ret);
+	}
+
+ 	return (' ');
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        main
+ *
+ * Purpose:     Unit test for functions above.
+ *
+ *----------------------------------------------------------------*/
+
+
+#if DTMF_TEST
+
+push_button (char button, int ms)
+{
+	static float phasea = 0;
+	static float phaseb = 0;
+	float fa, fb;
+	int i;
+	float input;
+	char x;
+	static char result[100];
+	static int result_len = 0;
+
+
+	switch (button) {
+	  case '1':  fa = dtmf_tones[0]; fb = dtmf_tones[4]; break;
+	  case '2':  fa = dtmf_tones[0]; fb = dtmf_tones[5]; break;
+	  case '3':  fa = dtmf_tones[0]; fb = dtmf_tones[6]; break;
+	  case 'A':  fa = dtmf_tones[0]; fb = dtmf_tones[7]; break;
+	  case '4':  fa = dtmf_tones[1]; fb = dtmf_tones[4]; break;
+	  case '5':  fa = dtmf_tones[1]; fb = dtmf_tones[5]; break;
+	  case '6':  fa = dtmf_tones[1]; fb = dtmf_tones[6]; break;
+	  case 'B':  fa = dtmf_tones[1]; fb = dtmf_tones[7]; break;
+	  case '7':  fa = dtmf_tones[2]; fb = dtmf_tones[4]; break;
+	  case '8':  fa = dtmf_tones[2]; fb = dtmf_tones[5]; break;
+	  case '9':  fa = dtmf_tones[2]; fb = dtmf_tones[6]; break;
+	  case 'C':  fa = dtmf_tones[2]; fb = dtmf_tones[7]; break;
+	  case '*':  fa = dtmf_tones[3]; fb = dtmf_tones[4]; break;
+	  case '0':  fa = dtmf_tones[3]; fb = dtmf_tones[5]; break;
+	  case '#':  fa = dtmf_tones[3]; fb = dtmf_tones[6]; break;
+	  case 'D':  fa = dtmf_tones[3]; fb = dtmf_tones[7]; break;
+	  case '?':
+
+	    if (strcmp(result, "123A456B789C*0#D123$789$") == 0) {
+	      dw_printf ("\nSuccess!\n");
+	    }
+	    else {
+	      dw_printf ("\n *** TEST FAILED ***\n");
+	      dw_printf ("\"%s\"\n", result);
+	    }
+	    break;
+
+	  default:  fa = 0; fb = 0;
+	}
+
+	for (i = 0; i < (ms*D.sample_rate)/1000; i++) {
+
+	  input = sin(phasea) + sin(phaseb);
+	  phasea += 2 * M_PI * fa / D.sample_rate;
+	  phaseb += 2 * M_PI * fb / D.sample_rate;
+
+	  /* Make sure it is insensitive to signal amplitude. */
+
+	  x = dtmf_sample (0, input);
+	  //x = dtmf_sample (0, input * 1000);
+	  //x = dtmf_sample (0, input * 0.001);
+
+	  if (x != ' ' && x != '.') {
+	    result[result_len] = x;
+	    result_len++;
+	    result[result_len] = '\0';
+	  }
+	}
+}
+
+main ()
+{
+
+	dtmf_init(44100);	
+
+	dw_printf ("\nFirst, check all button tone pairs. \n\n");
+	/* Max auto dialing rate is 10 per second. */
+
+	push_button ('1', 50); push_button (' ', 50);
+	push_button ('2', 50); push_button (' ', 50);
+	push_button ('3', 50); push_button (' ', 50);
+	push_button ('A', 50); push_button (' ', 50);
+
+	push_button ('4', 50); push_button (' ', 50);
+	push_button ('5', 50); push_button (' ', 50);
+	push_button ('6', 50); push_button (' ', 50);
+	push_button ('B', 50); push_button (' ', 50);
+
+	push_button ('7', 50); push_button (' ', 50);
+	push_button ('8', 50); push_button (' ', 50);
+	push_button ('9', 50); push_button (' ', 50);
+	push_button ('C', 50); push_button (' ', 50);
+
+	push_button ('*', 50); push_button (' ', 50);
+	push_button ('0', 50); push_button (' ', 50);
+	push_button ('#', 50); push_button (' ', 50);
+	push_button ('D', 50); push_button (' ', 50);
+
+	dw_printf ("\nShould reject very short pulses.\n\n");
+	
+	push_button ('1', 20); push_button (' ', 50);
+	push_button ('1', 20); push_button (' ', 50);
+	push_button ('1', 20); push_button (' ', 50);
+	push_button ('1', 20); push_button (' ', 50);
+	push_button ('1', 20); push_button (' ', 50);
+
+	dw_printf ("\nTest timeout after inactivity.\n\n");
+	/* For this test we use 1 second. */
+	/* In practice, it will probably more like 10 or 20. */
+
+	push_button ('1', 250); push_button (' ', 500);
+	push_button ('2', 250); push_button (' ', 500);
+	push_button ('3', 250); push_button (' ', 1200);
+
+	push_button ('7', 250); push_button (' ', 500);
+	push_button ('8', 250); push_button (' ', 500);
+	push_button ('9', 250); push_button (' ', 1200);
+
+	/* Check for expected results. */
+
+	push_button ('?', 0);
+
+}  /* end main */
+
+#endif
+
+/* end dtmf.c */
+
diff --git a/dtmf.h b/dtmf.h
new file mode 100755
index 0000000..78eb28a
--- /dev/null
+++ b/dtmf.h
@@ -0,0 +1,10 @@
+/* dtmf.h */
+
+
+void dtmf_init (int sample_rate);
+
+char dtmf_sample (int c, float input);
+
+
+/* end dtmf.h */
+
diff --git a/dw-icon.ico b/dw-icon.ico
new file mode 100755
index 0000000..00714be
Binary files /dev/null and b/dw-icon.ico differ
diff --git a/dw-icon.png b/dw-icon.png
new file mode 100755
index 0000000..4898018
Binary files /dev/null and b/dw-icon.png differ
diff --git a/dw-icon.rc b/dw-icon.rc
new file mode 100755
index 0000000..ce34b40
--- /dev/null
+++ b/dw-icon.rc
@@ -0,0 +1 @@
+MAINICON ICON "dw-icon.ico"
\ No newline at end of file
diff --git a/dw-start.sh b/dw-start.sh
new file mode 100755
index 0000000..b4829b4
--- /dev/null
+++ b/dw-start.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+
+#
+# Run this from crontab periodically to start up
+# Dire Wolf automatically.
+#
+# I prefer this method instead of putting something
+# in ~/.config/autostart.  That would start an application
+# only when the desktop first starts up.
+#
+# This method will restart the application if it
+# crashes or stops for any other reason.
+#
+# This script has some specifics the Raspberry Pi.
+# Some adjustments might be needed for other Linux variations.
+#
+# First wait a little while in case we just rebooted
+# and the desktop hasn't started up yet.
+#
+
+sleep 30
+
+#
+# Nothing to do if it is already running.
+#
+
+a=`ps -ef | grep direwolf | grep -v grep`
+if [ "$a" != "" ] 
+then
+  #date >> /tmp/dw-start.log
+  #echo "Already running." >> /tmp/dw-start.log
+  exit
+fi
+
+#
+# In my case, the Raspberry Pi is not connected to a monitor.
+# I access it remotely using VNC as described here:
+# http://learn.adafruit.com/adafruit-raspberry-pi-lesson-7-remote-control-with-vnc
+#
+# If VNC server is running, use its display number.
+# Otherwise default to :0.
+#
+
+date >> /tmp/dw-start.log
+
+export DISPLAY=":0"
+
+v=`ps -ef | grep Xtightvnc | grep -v grep`
+if [ "$v" != "" ]
+then
+  d=`echo "$v" | sed 's/.*tightvnc *\(:[0-9]\).*/\1/'`
+  export DISPLAY="$d"
+fi
+
+echo "DISPLAY=$DISPLAY" >> /tmp/dw-start.log
+
+echo "Start up application." >> /tmp/dw-start.log
+
+# 
+# Adjust for your particular situation:  gnome-terminal, xterm, etc.
+#
+
+if [ -x /usr/bin/lxterminal ]
+then
+  /usr/bin/lxterminal -t "Dire Wolf" -e "/usr/local/bin/direwolf" &
+elif [ -x /usr/bin/xterm ] 
+then
+  /usr/bin/xterm -bg white -fg black -e /usr/local/bin/direwolf &
+elif [ -x /usr/bin/x-terminal-emulator ]
+then
+  /usr/bin/x-terminal-emulator -e  /usr/local/bin/direwolf &
+else
+  echo "Did not find an X terminal emulator."
+fi
+
+echo "-----------------------" >> /tmp/dw-start.log
+
diff --git a/dwgps.c b/dwgps.c
new file mode 100755
index 0000000..2f5904a
--- /dev/null
+++ b/dwgps.c
@@ -0,0 +1,327 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      dwgps.c
+ *
+ * Purpose:   	Interface to location data, i.e. GPS receiver.
+ *		
+ * Description:	Tracker beacons need to know the current location.
+ *		At this time, I can't think of any other reason why
+ *		we would need this information.
+ *
+ *		For Linux, we use gpsd and libgps.
+ *		This has the extra benefit that the system clock can
+ *		be set from the GPS signal.
+ *
+ *		Not yet implemented for Windows.  Not sure how yet.
+ *		The Windows location API is new in Windows 7.
+ *		At the end of 2013, about 1/3 of Windows users are
+ *		still using XP so that still needs to be supported.	
+ *
+ * Reference:	
+ *
+ *---------------------------------------------------------------*/
+
+#if TEST
+#define ENABLE_GPS 1
+#endif
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#if __WIN32__
+#include <windows.h>
+#else
+#if ENABLE_GPS
+#include <gps.h>
+
+#if GPSD_API_MAJOR_VERSION != 5
+#error libgps API version might be incompatible.
+#endif
+
+#endif
+#endif
+
+#include "direwolf.h"
+#include "textcolor.h"
+#include "dwgps.h"
+
+
+/* Was init successful? */
+
+static enum { INIT_NOT_YET, INIT_SUCCESS, INIT_FAILED } init_status = INIT_NOT_YET;
+
+#if __WIN32__
+#include <windows.h>
+#else
+#if ENABLE_GPS
+
+static struct gps_data_t gpsdata;
+
+#endif
+#endif
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        dwgps_init
+ *
+ * Purpose:    	Intialize the GPS interface.
+ *
+ * Inputs:	none.
+ *		
+ * Returns:	0 = success
+ *		-1 = failure
+ *
+ * Description:	For Linux, this maps into gps_open.
+ *		Not yet implemented for Windows.
+ *
+ *--------------------------------------------------------------------*/
+
+int dwgps_init (void)
+{
+
+#if __WIN32__
+
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf ("GPS interface not yet available in Windows version.\n");
+	init_status = INIT_FAILED;
+	return (-1);
+
+#elif ENABLE_GPS
+
+	int err;
+
+	err = gps_open (GPSD_SHARED_MEMORY, NULL, &gpsdata);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Unable to connect to GPS receiver.\n");
+	  if (err == NL_NOHOST) {
+	    dw_printf ("Shared memory interface is not enabled in libgps.\n");
+	    dw_printf ("Download the gpsd source and build with 'shm_export=True' option.\n");
+	  }
+	  else {
+	    dw_printf ("%s\n", gps_errstr(errno));
+	  }
+	  init_status = INIT_FAILED;
+	  return (-1);
+	}
+	init_status = INIT_SUCCESS;
+	return (0);
+#else
+
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf ("GPS interface not enabled in this version.\n");
+	dw_printf ("See documentation on how to rebuild with ENABLE_GPS.\n");
+	init_status = INIT_FAILED;
+	return (-1);
+
+#endif
+
+}  /* end dwgps_init */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        dwgps_read
+ *
+ * Purpose:    	Obtain current location from GPS receiver.
+ *
+ * Outputs:	*plat		- Latitude.
+ *		*plon		- Longitude.
+ *		*pspeed		- Speed, knots.
+ *		*pcourse	- Course over ground, degrees.
+ *		*palt		- Altitude, meters.
+ *
+ * Returns:	-1 = error
+ *		0 = data not available (no fix)
+ *		2 = 2D fix, lat/lon, speed, and course are set.
+ *		3 - 3D fix, altitude is also set.
+ *
+ *--------------------------------------------------------------------*/
+
+int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float *palt)
+{
+#if __WIN32__
+
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf ("Internal error, dwgps_read, shouldn't be here.\n");
+	return (-1);
+
+#elif ENABLE_GPS
+
+	int err;
+
+	if (init_status != INIT_SUCCESS) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Internal error, dwgps_read without successful init.\n");
+	  return (-1);
+	}
+
+	err = gps_read (&gpsdata);
+
+#if DEBUG
+	dw_printf ("gps_read returns %d bytes\n", err);
+#endif
+	if (err > 0) {
+	  if (gpsdata.status >= STATUS_FIX && gpsdata.fix.mode >= MODE_2D) {
+
+	     *plat = gpsdata.fix.latitude;
+	     *plon = gpsdata.fix.longitude;
+	     *pcourse = gpsdata.fix.track;		
+	     *pspeed = MPS_TO_KNOTS * gpsdata.fix.speed; /* libgps uses meters/sec */
+
+	     if (gpsdata.fix.mode >= MODE_3D) {
+	       *palt = gpsdata.fix.altitude;
+	       return (3);
+	     }
+	     return (2);
+	   }
+
+	   /* No fix.  Probably temporary condition. */
+	   return (0);
+	}
+	else if (err == 0) {
+	   /* No data available */
+	   return (0);
+	}
+	else {
+	  /* More serious error. */
+	  return (-1);
+	}
+#else 
+
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf ("Internal error, dwgps_read, shouldn't be here.\n");
+	return (-1);
+#endif
+
+} /* end dwgps_read */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        dwgps_term
+ *
+ * Purpose:    	Shut down GPS interface before exiting from application.
+ *
+ * Inputs:	none.
+ *
+ * Returns:	none.
+ *
+ *--------------------------------------------------------------------*/
+
+void dwgps_term (void) {
+
+#if __WIN32__
+	
+#elif ENABLE_GPS
+
+	if (init_status == INIT_SUCCESS) {
+	  gps_close (&gpsdata);
+	}
+#else 
+
+#endif
+
+} /* end dwgps_term */
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        main
+ *
+ * Purpose:    	Simple unit test for other functions in this file.
+ *
+ * Description: Compile with -DTEST option.
+ *
+ *			gcc -DTEST dwgps.c textcolor.c -lgps
+ *
+ *--------------------------------------------------------------------*/
+
+#if TEST
+
+int main (int argc, char *argv[])
+{
+
+#if __WIN32__
+
+	printf ("Not in win32 version yet.\n");
+
+#elif ENABLE_GPS
+	int err;
+	int fix;
+	double lat;
+	double lon;
+	float speed;
+	float course;
+	float alt;
+
+	err = dwgps_init ();
+
+	if (err != 0) exit(1);
+
+	while (1) {
+	  fix = dwgps_read (&lat, &lon, &speed, &course, &alt)
;
+	  switch (fix) {
+	    case 3:
+	    case 2:
+	      dw_printf ("%.6f  %.6f", lat, lon);
+	      dw_printf ("  %.1f knots  %.0f degrees", speed, course);
+	      if (fix==3) dw_printf ("  altitude = %.1f meters", alt);
+	      dw_printf ("\n");
+	      break;
+	    case 0:
+	      dw_printf ("location currently not available.\n");
+	      break;
+	    default:
+	      dw_printf ("ERROR getting GPS information.\n");
+	  }
+	  sleep (3);
+	}
+
+
+#else 
+
+	printf ("Test: Shouldn't be here.\n");
+#endif
+
+} /* end main */
+
+
+#endif
+
+
+
+/* end dwgps.c */
+
+
+
diff --git a/dwgps.h b/dwgps.h
new file mode 100755
index 0000000..ffb563d
--- /dev/null
+++ b/dwgps.h
@@ -0,0 +1,15 @@
+
+/* dwgps.h */
+
+
+int dwgps_init (void);
+
+int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float *palt);
+
+void dwgps_term (void);
+
+
+/* end dwgps.h */
+
+
+
diff --git a/encode_aprs.c b/encode_aprs.c
new file mode 100755
index 0000000..638ac2d
--- /dev/null
+++ b/encode_aprs.c
@@ -0,0 +1,797 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+/*------------------------------------------------------------------
+ *
+ * Module:      encode_aprs.c
+ *
+ * Purpose:   	Construct APRS packets from components.
+ *		
+ * Description: 
+ *
+ * References:	APRS Protocol Reference.
+ *
+ *		Frequency spec.
+ *		http://www.aprs.org/info/freqspec.txt
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+#include <assert.h>
+
+#include "direwolf.h"
+#include "encode_aprs.h"
+#include "latlong.h"
+#include "textcolor.h"
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        set_norm_position
+ *
+ * Purpose:     Fill in the human-readable latitude, longitude, 
+ * 		symbol part which is common to multiple data formats.
+ *
+ * Inputs: 	symtab	- Symbol table id or overlay.
+ *		symbol	- Symbol id.
+ *    		dlat	- Latitude.
+ *		dlong	- Longitude.
+ *
+ * Outputs:	presult	- Stored here.  
+ *
+ * Returns:     Number of characters in result.
+ *
+ *----------------------------------------------------------------*/
+
+/* Position & symbol fields common to several message formats. */
+
+typedef struct position_s {
+	  char lat[8];
+	  char sym_table_id;		/* / \ 0-9 A-Z */
+	  char lon[9];
+	  char symbol_code;
+	} position_t;
+
+
+static int set_norm_position (char symtab, char symbol, double dlat, double dlong, position_t *presult)
+{
+
+	latitude_to_str (dlat, 0, presult->lat);
+
+	if (symtab != '/' && symtab != '\\' && ! isdigit(symtab) && ! isupper(symtab)) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Symbol table identifier is not one of / \\ 0-9 A-Z\n");
+	}
+	presult->sym_table_id = symtab;
+
+	longitude_to_str (dlong, 0, presult->lon);
+
+	if (symbol < '!' || symbol > '~') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Symbol code is not in range of ! to ~\n");
+	}
+	presult->symbol_code = symbol;
+
+	return (sizeof(position_t));
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        set_comp_position
+ *
+ * Purpose:     Fill in the compressed latitude, longitude, 
+ *		symbol part which is common to multiple data formats.
+ *
+ * Inputs: 	symtab	- Symbol table id or overlay.
+ *		symbol	- Symbol id.
+ *    		dlat	- Latitude.
+ *		dlong	- Longitude.
+ *
+ * 	 	power	- Watts.
+ *		height	- Feet.
+ *		gain	- dBi.
+ *
+ * 		course	- Degress, 1 - 360.  0 means none or unknown.
+ *		speed	- knots.
+ *
+ *
+ * Outputs:	presult	- Stored here.  
+ *
+ * Returns:     Number of characters in result.
+ *
+ * Description:	The cst field can have only one of 
+ *
+ *		course/speed	- takes priority (this implementation)
+ *		radio range	- calculated from PHG
+ *		altitude	- not implemented yet.
+ *
+ *----------------------------------------------------------------*/
+
+/* Compressed position & symbol fields common to several message formats. */
+
+typedef struct compressed_position_s {
+	  char sym_table_id;		/* / \ a-j A-Z */
+					/* "The presence of the leading Symbol Table Identifier */
+					/* instead of a digit indicates that this is a compressed */
+					/* Position Report and not a normal lat/long report." */
+
+	  char y[4];			/* Compressed Latitude. */
+	  char x[4];			/* Compressed Longitude. */
+	  char symbol_code;
+	  char c;			/* Course/speed or radio range or altitude. */
+	  char s;
+	  char t	;		/* Compression type. */
+	} compressed_position_t;
+
+
+static int set_comp_position (char symtab, char symbol, double dlat, double dlong, 
+		int power, int height, int gain, 
+		int course, int speed,
+		compressed_position_t *presult)
+{
+
+	if (symtab != '/' && symtab != '\\' && ! isdigit(symtab) && ! isupper(symtab)) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Symbol table identifier is not one of / \\ 0-9 A-Z\n");
+	}
+
+/*
+ * In compressed format, the characters a-j are used for a numeric overlay.
+ * This allows the receiver to distinguish between compressed and normal formats.
+ */
+	if (isdigit(symtab)) {
+	  symtab = symtab - '0' + 'a';
+	}
+	presult->sym_table_id = symtab;
+
+	latitude_to_comp_str (dlat, presult->y);
+	longitude_to_comp_str (dlong, presult->x);
+
+	if (symbol < '!' || symbol > '~') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Symbol code is not in range of ! to ~\n");
+	}
+	presult->symbol_code = symbol;
+
+/*
+ * The cst field is complicated.
+ *
+ * When c is ' ', the cst field is not used.
+ *
+ * When the t byte has a certain pattern, c & s represent altitude.
+ *
+ * Otherwise, c & s can be either course/speed or radio range.
+ *
+ * When c is in range of '!' to 'z', 
+ *
+ * 	('!' - 33) * 4 = 0 degrees.
+ *	...
+ *	('z' - 33) * 4 = 356 degrees.
+ *
+ * In this case, s represents speed ...
+ *
+ * When c is '{', s is range ...
+ */
+
+	if (course || speed) {
+	  int c;
+	  int s;
+
+	  c = (course + 1) / 4;
+	  if (c < 0) c += 90;
+	  if (c >= 90) c -= 90;
+	  presult->c = c + '!';
+
+	  s = (int)round(log(speed+1.0) / log(1.08));
+	  presult->s = s + '!';
+	   
+	  presult->t = 0x26 + '!';	/* current, other tracker. */
+	}
+	else if (power || height || gain) {
+	  int s;
+	  float range;
+
+	  presult->c = '{';		/* radio range. */
+
+	  if (power == 0) power = 10;
+	  if (height == 0) height = 20;
+	  if (gain == 0) gain = 3;
+
+	  // from protocol reference page 29.
+	  range = sqrt(2.0*height * sqrt((power/10.0) * (gain/2.0)));
+
+	  s = (int)round(log(range/2.) / log(1.08));
+	  if (s < 0) s = 0;
+	  if (s > 93) s = 93;
+
+	  presult->s = s + '!';
+   
+	  presult->t = 0x26 + '!';	/* current, other tracker. */  
+	}
+	else {
+	  presult->c = ' ';		/* cst field not used. */
+	  presult->s = ' ';
+	  presult->t = '!';		/* avoid space. */  
+	}
+	return (sizeof(compressed_position_t));
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        phg_data_extension
+ *
+ * Purpose:     Fill in parts of the power/height/gain data extension.
+ *
+ * Inputs: 	power	- Watts.
+ *		height	- Feet.
+ *		gain	- dB.  Not clear if it is dBi or dBd.
+ *		dir	- Directivity: N, NE, etc., omni.
+ *
+ * Outputs:	presult	- Stored here.  
+ *
+ * Returns:     Number of characters in result.
+ *
+ *----------------------------------------------------------------*/
+
+
+typedef struct phg_s {
+	  char P;
+	  char H;
+	  char G;
+	  char p;
+	  char h;
+	  char g;
+	  char d;
+	} phg_t;
+
+
+static int phg_data_extension (int power, int height, int gain, char *dir, char *presult)
+{
+	phg_t *r = (phg_t*)presult;
+	int x;
+
+	r->P = 'P';
+	r->H = 'H';
+	r->G = 'G';
+		
+	x = (int)round(sqrt((float)power)) + '0';
+	if (x < '0') x = '0';
+	else if (x > '9') x = '9';
+	r->p = x;
+
+	x = (int)round(log2(height/10.0)) + '0';
+	if (x < '0') x = '0';
+	/* Result can go beyond '9'. */
+	r->h = x;
+
+	x = gain + '0';
+	if (x < '0') x = '0';
+	else if (x > '9') x = '0';
+	r->g = x;
+
+	r->d = '0';
+	if (dir != NULL) {
+	  if (strcasecmp(dir,"NE") == 0) r->d = '1';
+	  if (strcasecmp(dir,"E") == 0) r->d = '2';
+	  if (strcasecmp(dir,"SE") == 0) r->d = '3';
+	  if (strcasecmp(dir,"S") == 0) r->d = '4';
+	  if (strcasecmp(dir,"SW") == 0) r->d = '5';
+	  if (strcasecmp(dir,"W") == 0) r->d = '6';
+	  if (strcasecmp(dir,"NW") == 0) r->d = '7';
+	  if (strcasecmp(dir,"N") == 0) r->d = '8';
+	}
+	return (sizeof(phg_t));
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        cse_spd_data_extension
+ *
+ * Purpose:     Fill in parts of the course & speed data extension.
+ *
+ * Inputs: 	course	- Degress, 1 - 360.
+ *		speed	- knots.
+ *
+ * Outputs:	presult	- Stored here.  
+ *
+ * Returns:     Number of characters in result.
+ *
+ *----------------------------------------------------------------*/
+
+
+typedef struct cs_s {
+	  char cse[3];
+	  char slash;
+	  char spd[3];
+	} cs_t;
+
+
+static int cse_spd_data_extension (int course, int speed, char *presult)
+{
+	cs_t *r = (cs_t*)presult;
+	char stemp[8];
+	int x;
+
+	x = course;
+	if (x < 0) x = 0;
+	if (x > 360) x = 360;
+	sprintf (stemp, "%03d", x);
+	memcpy (r->cse, stemp, 3);
+
+	r->slash = '/';
+
+	x = speed;
+	if (x < 0) x = 0;
+	if (x > 999) x = 999;
+	sprintf (stemp, "%03d", x);
+	memcpy (r->spd, stemp, 3);
+
+	return (sizeof(cs_t));
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        frequency_spec
+ *
+ * Purpose:     Put frequency specification in beginning of comment field.
+ *
+ * Inputs: 	freq	- MHz.
+ *		tone	- Hz.
+ *		offset	- MHz.
+ *
+ * Outputs:	presult	- Stored here.  
+ *
+ * Returns:     Number of characters in result.
+ *
+ * Description:	There are several valid variations.
+ *
+ *		The frequency could be missing here if it is in the 
+ *		object name.  In this case we could have tone & offset.
+ *
+ *		Offset must always be preceded by tone.
+ *
+ *----------------------------------------------------------------*/
+
+
+typedef struct freq_s {
+	  char f[7];		/* format 999.999 */
+	  char mhz[3];
+	  char space;
+	} freq_t;
+
+typedef struct to_s {
+	  char T;
+	  char ttt[3];		/* format 999 (drop fraction) or 'off'. */
+	  char space1;
+	  char oooo[4];		/* leading sign, 3 digits, tens of KHz. */
+	  char space2;
+	} to_t;
+
+
+static int frequency_spec (float freq, float tone, float offset, char *presult)
+{
+	int result_len = 0;
+	
+	if (freq != 0) {
+	  freq_t *f = (freq_t*)presult;
+	  char stemp[12];
+
+	  /* Should use letters for > 999.999. */
+	  sprintf (stemp, "%07.3f", freq);
+	  memcpy (f->f, stemp, 7);
+	  memcpy (f->mhz, "MHz", 3);
+	  f->space = ' ';
+	  result_len = sizeof (freq_t);
+	}
+	
+	if (tone != 0 || offset != 0) {
+	  to_t *to = (to_t*)(presult + result_len);
+	  char stemp[12];
+
+	  to->T = 'T';
+	  if (tone == 0) {
+	    memcpy(to->ttt, "off", 3);
+	  }
+	  else {
+	    sprintf (stemp, "%03d", (int)tone);
+	    memcpy (to->ttt, stemp, 3);
+	  }
+	  to->space1 = ' ';
+	  sprintf (stemp, "%+04d", (int)round(offset * 100));
+	  memcpy (to->oooo, stemp, 4);
+	  to->space2 = ' ';
+
+	  result_len += sizeof (to_t);
+	}
+
+	return (result_len);
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        encode_position
+ *
+ * Purpose:     Construct info part for position report format.
+ *
+ * Inputs:      compressed - Send in compressed form?
+ *		lat	- Latitude.
+ *		lon	- Longitude.
+ *		symtab	- Symbol table id or overlay.
+ *		symbol	- Symbol id.
+ *
+ * 	 	power	- Watts.
+ *		height	- Feet.
+ *		gain	- dB.  Not clear if it is dBi or dBd.
+ *		dir	- Directivity: N, NE, etc., omni.
+ *
+ * 		course	- Degress, 1 - 360.  0 means none or unknown.
+ *		speed	- knots.
+ *
+ * 	 	freq	- MHz.
+ *		tone	- Hz.
+ *		offset	- MHz.
+ *
+ *		comment	- Additional comment text.
+ *
+ *
+ * Outputs:	presult	- Stored here.  Should be at least ??? bytes.
+ *
+ * Returns:     Number of characters in result.
+ *
+ * Description:	There can be a single optional "data extension"
+ *		following the position so there is a choice
+ *		between:
+ *			Power/height/gain/directivity or
+ *			Course/speed.
+ *
+ *		Afer that, 
+ *
+ *----------------------------------------------------------------*/
+
+typedef struct aprs_ll_pos_s {
+	  char dti;			/* ! or = */
+	  position_t pos;
+	  				/* Comment up to 43 characters. */
+					/* Start of comment could be data extension(s). */
+} aprs_ll_pos_t;
+
+
+typedef struct aprs_compressed_pos_s {
+	  char dti;			/* ! or = */
+	  compressed_position_t cpos;
+	  				/* Comment up to 40 characters. */
+					/* No data extension allowed for compressed location. */
+} aprs_compressed_pos_t;
+
+
+int encode_position (int compressed, double lat, double lon, 
+		char symtab, char symbol, 
+		int power, int height, int gain, char *dir,
+		int course, int speed,
+		float freq, float tone, float offset,
+		char *comment,
+		char *presult)
+{
+	int result_len = 0;
+
+	if (compressed) {
+	  aprs_compressed_pos_t *p = (aprs_compressed_pos_t *)presult;
+
+	  p->dti = '!';
+	  set_comp_position (symtab, symbol, lat, lon, 
+		power, height, gain, 
+		course, speed,
+		&(p->cpos));
+	  result_len = 1 + sizeof (p->cpos);
+	}
+	else {
+	  aprs_ll_pos_t *p = (aprs_ll_pos_t *)presult;
+
+	  p->dti = '!';
+	  set_norm_position (symtab, symbol, lat, lon, &(p->pos));
+	  result_len = 1 + sizeof (p->pos);
+
+/* Optional data extension. (singular) */
+/* Can't have both course/speed and PHG.  Former gets priority. */
+
+	  if (course || speed) {
+	    result_len += cse_spd_data_extension (course, speed, presult + result_len);
+	  }
+	  else if (power || height || gain) {
+ 	    result_len += phg_data_extension (power, height, gain, dir, presult + result_len);
+	  }
+	}
+
+/* Optional frequency spec. */
+
+	if (freq != 0 || tone != 0 || offset != 0) {
+	  result_len += frequency_spec (freq, tone, offset, presult + result_len);
+	}
+
+	presult[result_len] = '\0';
+
+/* Finally, comment text. */
+	
+	if (comment != NULL) {
+	  strcat (presult, comment);
+	  result_len += strlen(comment);
+	}
+
+	return (result_len);
+
+} /* end encode_position */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        encode_object
+ *
+ * Purpose:     Construct info part for object report format.
+ *
+ * Inputs:      name	- Name, up to 9 characters.
+ *		compressed - Send in compressed form?
+ *		thyme	- Time stamp or 0 for none.
+ *		lat	- Latitude.
+ *		lon	- Longitude.
+ *		symtab	- Symbol table id or overlay.
+ *		symbol	- Symbol id.
+ *
+ * 	 	power	- Watts.
+ *		height	- Feet.
+ *		gain	- dB.  Not clear if it is dBi or dBd.
+ *		dir	- Direction: N, NE, etc., omni.
+ *
+ * 		course	- Degress, 1 - 360.  0 means none or unknown.
+ *		speed	- knots.
+ *
+ * 	 	freq	- MHz.
+ *		tone	- Hz.
+ *		offset	- MHz.
+ *
+ *		comment	- Additional comment text.
+ *
+ * Outputs:	presult	- Stored here.  Should be at least ??? bytes.
+ *
+ * Returns:     Number of characters in result.
+ *
+ * Description:	
+ *
+ *----------------------------------------------------------------*/
+
+typedef struct aprs_object_s {
+	  struct {
+	    char dti;			/* ; */					
+	    char name[9];								
+	    char live_killed;		/* * for live or _ for killed */	
+	    char time_stamp[7];	  
+	  } o;
+	  union {
+	    position_t pos;		/* Up to 43 char comment.  First 7 bytes could be data extension. */
+	    compressed_position_t cpos;	/* Up to 40 char comment.  No PHG data extension in this case. */
+	  } u;    
+	} aprs_object_t;
+
+int encode_object (char *name, int compressed, time_t thyme, double lat, double lon, 
+		char symtab, char symbol, 
+		int power, int height, int gain, char *dir,
+		int course, int speed,
+		float freq, float tone, float offset, char *comment,
+		char *presult)
+{
+	aprs_object_t *p = (aprs_object_t *) presult;
+	int result_len = 0;
+	int n;
+
+
+	p->o.dti = ';';
+
+	memset (p->o.name, ' ', sizeof(p->o.name));
+	n = strlen(name);
+	if (n > sizeof(p->o.name)) n = sizeof(p->o.name);
+	memcpy (p->o.name, name, n);
+
+	p->o.live_killed = '*';
+
+	if (thyme != 0) {
+	  struct tm tm;
+
+#define XMIT_UTC 1
+#if XMIT_UTC
+	  gmtime_r (&thyme, &tm);
+#else
+	  /* Using local time, for this application, would make more sense to me. */
+	  /* On Windows, localtime_r produces UTC. */
+	  /* How do we set the time zone?  Google for mingw time zone. */
+
+	  localtime_r (thyme, &tm);
+#endif
+	  sprintf (p->o.time_stamp, "%02d%02d%02d", tm.tm_mday, tm.tm_hour, tm.tm_min);
+#if XMIT_UTC
+	  p->o.time_stamp[6] = 'z';
+#else
+	  p->o.time_stamp[6] = '/';
+#endif
+	}
+	else {
+	  memcpy (p->o.time_stamp, "111111z", sizeof(p->o.time_stamp));
+	}
+
+	if (compressed) {
+	  set_comp_position (symtab, symbol, lat, lon, 
+		power, height, gain, 
+		course, speed,
+		&(p->u.cpos));
+	  result_len = sizeof(p->o) + sizeof (p->u.cpos);
+	}
+	else {
+	  set_norm_position (symtab, symbol, lat, lon, &(p->u.pos));
+	  result_len = sizeof(p->o) + sizeof (p->u.pos);
+
+/* Optional data extension. (singular) */
+/* Can't have both course/speed and PHG.  Former gets priority. */
+
+	  if (course || speed) {
+	    result_len += cse_spd_data_extension (course, speed, presult + result_len);
+	  }
+	  else if (power || height || gain) {
+ 	    result_len += phg_data_extension (power, height, gain, dir, presult + result_len);
+	  }
+	}
+
+/* Optional frequency spec. */
+
+	if (freq != 0 || tone != 0 || offset != 0) {
+	  result_len += frequency_spec (freq, tone, offset, presult + result_len);
+	}
+
+	presult[result_len] = '\0';
+
+/* Finally, comment text. */
+	
+	if (comment != NULL) {
+	  strcat (presult, comment);
+	  result_len += strlen(comment);
+	}
+
+	return (result_len);
+
+} /* end encode_object */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        main
+ *
+ * Purpose:     Quick test for some functions in this file.
+ *
+ * Description:	Just a smattering, not an organized test.
+ *
+ * 		$ rm a.exe ; gcc -DEN_MAIN encode_aprs.c latlong.c ; ./a.exe
+ *
+ *----------------------------------------------------------------*/
+
+
+#if EN_MAIN
+
+void text_color_set ( enum dw_color_e c )
+{
+        return;
+} 
+
+int main (int argc, char *argv[])
+{
+	char result[100];
+
+
+
+/***********  Position  ***********/
+
+	encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', 
+		0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result);
+	dw_printf ("%s\n", result);
+	if (strcmp(result, "!4234.61ND07126.47W&") != 0) dw_printf ("ERROR!\n");
+
+/* with PHG. */
+
+	encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', 
+		50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result);
+	dw_printf ("%s\n", result);
+	if (strcmp(result, "!4234.61ND07126.47W&PHG7368") != 0) dw_printf ("ERROR!\n");
+
+/* with freq. */
+
+	encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', 
+		0, 0, 0, NULL, 0, 0, 146.955, 74.4, -0.6, NULL, result);
+	dw_printf ("%s\n", result);
+	if (strcmp(result, "!4234.61ND07126.47W&146.955MHz T074 -060 ") != 0) dw_printf ("ERROR!\n");
+
+/* with course/speed, freq, and comment! */
+
+	encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', 
+		0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result);
+	dw_printf ("%s\n", result);
+	if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR!\n");
+
+/* Course speed, no tone, + offset */
+
+	encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', 
+		0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result);
+	dw_printf ("%s\n", result);
+	if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 River flooding") != 0) dw_printf ("ERROR!\n");
+
+
+
+
+
+/*********** Compressed position. ***********/
+
+	encode_position (1, 42+34.61/60, -(71+26.47/60), 'D', '&', 
+		0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result);
+	dw_printf ("%s\n", result);
+	if (strcmp(result, "!D8yKC<Hn[&   ") != 0) dw_printf ("ERROR!\n");
+
+
+/* with PHG. In this case it is converted to precomputed radio range. */
+
+	encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', 
+		50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result);
+	dw_printf ("%s\n", result);
+	if (strcmp(result, "!4234.61ND07126.47W&PHG7368   TBD ???") != 0) dw_printf ("ERROR!\n");
+
+/* with course/speed, freq, and comment! */
+
+	encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', 
+		0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result);
+	dw_printf ("%s\n", result);
+	if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR!\n");
+
+
+
+/*********** Object. ***********/
+
+
+	encode_object ("WB1GOF-C", 0, 0, 42+34.61/60, -(71+26.47/60), 'D', '&', 
+		0, 0, 0, NULL, result);
+	dw_printf ("%s\n", result);
+	if (strcmp(result, ";WB1GOF-C *111111z4234.61ND07126.47W&   TBD???") != 0) dw_printf ("ERROR!\n");
+
+
+	return(0);
+
+}  /* end main */
+
+#endif		/* unit test */
+
+
+/* end encode_aprs.c */
+
diff --git a/encode_aprs.h b/encode_aprs.h
new file mode 100755
index 0000000..1d3c828
--- /dev/null
+++ b/encode_aprs.h
@@ -0,0 +1,16 @@
+
+int encode_position (int compressed, double lat, double lon, 
+		char symtab, char symbol, 
+		int power, int height, int gain, char *dir,
+		int course, int speed,
+		float freq, float tone, float offset,
+		char *comment,
+		char *presult);
+
+int encode_object (char *name, int compressed, time_t thyme, double lat, double lon, 
+		char symtab, char symbol, 
+		int power, int height, int gain, char *dir,
+		int course, int speed,
+		float freq, float tone, float offset, char *comment,
+		char *presult);
+
diff --git a/fcs_calc.c b/fcs_calc.c
new file mode 100755
index 0000000..d2e9cfe
--- /dev/null
+++ b/fcs_calc.c
@@ -0,0 +1,108 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+#include <stdio.h>
+
+/*
+ * Calculate the FCS for an AX.25 frame.
+ */
+
+#include "fcs_calc.h"
+
+
+static const unsigned short ccitt_table[256] = {
+
+// from http://www.ietf.org/rfc/rfc1549.txt
+
+   0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+   0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+   0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+   0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+   0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+   0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+   0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+   0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+   0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+   0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+   0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+   0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+   0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+   0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+   0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+   0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+   0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+   0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+   0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+   0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+   0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+   0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+   0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+   0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+   0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+   0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+   0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+   0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+   0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+   0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+   0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+   0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+
+};
+
+
+/* 
+ * Use this for an AX.25 frame. 
+ */
+
+unsigned short fcs_calc (unsigned char *data, int len)
+{
+	unsigned short crc = 0xffff;
+	int j;
+
+	for (j=0; j<len; j++) {
+
+  	  crc = ((crc) >> 8) ^ ccitt_table[((crc) ^ data[j]) & 0xff];
+	}
+
+	return ( crc ^ 0xffff );
+}
+
+
+/* 
+ * This can be used when we want to calculate a single CRC over disjoint data.
+ *
+ * 	crc = crc16 (region1, sizeof(region1), 0xffff);
+ *	crc = crc16 (region2, sizeof(region2), crc);
+ *	crc = crc16 (region3, sizeof(region3), crc);
+ */
+
+unsigned short crc16 (unsigned char *data, int len, unsigned short seed)
+{
+	unsigned short crc = seed;
+	int j;
+
+	for (j=0; j<len; j++) {
+
+  	  crc = ((crc) >> 8) ^ ccitt_table[((crc) ^ data[j]) & 0xff];
+	}
+
+	return ( crc ^ 0xffff );
+}
+
diff --git a/fcs_calc.h b/fcs_calc.h
new file mode 100755
index 0000000..fa0c34b
--- /dev/null
+++ b/fcs_calc.h
@@ -0,0 +1,11 @@
+
+/* fcs_calc.h */
+
+
+unsigned short fcs_calc (unsigned char *data, int len);
+
+unsigned short crc16 (unsigned char *data, int len, unsigned short seed);
+
+/* end fcs_calc.h */
+
+
diff --git a/fsk_demod_agc.h b/fsk_demod_agc.h
new file mode 100755
index 0000000..c4353c9
--- /dev/null
+++ b/fsk_demod_agc.h
@@ -0,0 +1,2 @@
+#define TUNE_MS_FILTER_SIZE 140 
+#define TUNE_PRE_BAUD    1.080 
diff --git a/fsk_demod_state.h b/fsk_demod_state.h
new file mode 100755
index 0000000..9185361
--- /dev/null
+++ b/fsk_demod_state.h
@@ -0,0 +1,172 @@
+/* fsk_demod_state.h */
+
+#ifndef FSK_DEMOD_STATE_H
+
+/*
+ * Demodulator state.
+ * Different copy is required for each channel & subchannel being processed concurrently.
+ */
+
+
+typedef enum bp_window_e { BP_WINDOW_TRUNCATED, 
+				BP_WINDOW_COSINE, 
+				BP_WINDOW_HAMMING,
+				BP_WINDOW_BLACKMAN,
+				BP_WINDOW_FLATTOP } bp_window_t;
+
+struct demodulator_state_s
+{
+/*
+ * These are set once during initialization.
+ */
+
+#define TICKS_PER_PLL_CYCLE ( 256.0 * 256.0 * 256.0 * 256.0 )
+
+	int pll_step_per_sample;	// PLL is advanced by this much each audio sample.
+					// Data is sampled when it overflows.
+
+
+	int ms_filter_size;		/* Size of mark & space filters, in audio samples. */
+					/* Started off as a guess of one bit length */
+					/* but somewhat longer turned out to be better. */
+					/* Currently using same size for any prefilter. */
+
+#define MAX_FILTER_SIZE 320		/* 304 is needed for profile C, 300 baud & 44100. */
+
+/*
+ * FIR filter length relative to one bit time.
+ * Use same for both bandpass and lowpass.
+ */
+	float filter_len_bits;
+
+/* 
+ * Window type for the mark/space filters.
+ */
+	bp_window_t bp_window;
+
+/*
+ * Alternate Low pass filters.
+ * First is arbitrary number for quick IIR.
+ * Second is frequency as ratio to baud rate for FIR.
+ */
+	int lpf_use_fir;		/* 0 for IIR, 1 for FIR. */
+	float lpf_iir;
+	float lpf_baud;
+
+/*
+ * Automatic gain control.  Fast attack and slow decay factors.
+ */
+	float agc_fast_attack;
+	float agc_slow_decay;
+/*
+ * Hysteresis before final demodulator 0 / 1 decision.		
+ */
+	float hysteresis;
+
+/* 
+ * Phase Locked Loop (PLL) inertia.
+ * Larger number means less influence by signal transitions.
+ */
+	float pll_locked_inertia;
+	float pll_searching_inertia;
+			
+
+/*
+ * Optional band pass pre-filter before mark/space detector.
+ */
+	int use_prefilter;	/* True to enable it. */
+
+	float prefilter_baud;	/* Cutoff frequencies, as fraction of */
+				/* baud rate, beyond tones used.  */
+				/* Example, if we used 1600/1800 tones at */
+				/* 300 baud, and this was 0.5, the cutoff */
+				/* frequencies would be: */
+				/* lower = min(1600,1800) - 0.5 * 300 = 1450 */
+				/* upper = max(1600,1800) + 0.5 * 300 = 1950 */
+
+	float pre_filter[MAX_FILTER_SIZE] __attribute__((aligned(16)));
+
+/*
+ * Kernel for the mark and space detection filters.
+ */
+					
+	float m_sin_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
+	float m_cos_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
+
+	float s_sin_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
+	float s_cos_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
+
+/*
+ * The rest are continuously updated.
+ */
+	signed int data_clock_pll;		// PLL for data clock recovery.
+						// It is incremented by pll_step_per_sample
+						// for each audio sample.
+
+	signed int prev_d_c_pll;		// Previous value of above, before
+						// incrementing, to detect overflows.
+
+/*
+ * Most recent raw audio samples, before/after prefiltering.
+ */
+	float raw_cb[MAX_FILTER_SIZE] __attribute__((aligned(16)));
+
+/*
+ * Input to the mark/space detector.
+ * Could be prefiltered or raw audio.
+ */
+	float ms_in_cb[MAX_FILTER_SIZE] __attribute__((aligned(16)));
+
+/*
+ * Outputs from the mark and space amplitude detection, 
+ * used as inputs to the FIR lowpass filters.
+ * Kernel for the lowpass filters.
+ */
+
+	int lp_filter_size;
+
+	float m_amp_cb[MAX_FILTER_SIZE] __attribute__((aligned(16)));
+	float s_amp_cb[MAX_FILTER_SIZE] __attribute__((aligned(16)));
+
+	float lp_filter[MAX_FILTER_SIZE] __attribute__((aligned(16)));
+
+
+	float m_peak, s_peak;
+	float m_valley, s_valley;
+	float m_amp_prev, s_amp_prev;
+
+	int prev_demod_data;			// Previous data bit detected.
+						// Used to look for transitions.
+
+
+/* These are used only for "9600" baud data. */
+
+	int lfsr;				// Descrambler shift register.
+
+
+/* 
+ * Finally, try to come up with some sort of measure of the audio input level. 
+ * Let's try gathering both the peak and average of the 
+ * absolute value of the input signal over some period such as 100 mS.
+ * 
+ */
+	int lev_period;				// How many samples go into one measure.
+
+	int lev_count;				// Number accumulated so far.
+
+	float lev_peak_acc;			// Highest peak so far.
+
+	float lev_sum_acc;			// Accumulated sum so far.
+
+/*
+ * These will be updated every 'lev_period' samples:
+ */
+	float lev_last_peak;
+	float lev_last_ave;
+	float lev_prev_peak;
+	float lev_prev_ave;
+
+};
+
+#define FSK_DEMOD_STATE_H 1
+#endif
\ No newline at end of file
diff --git a/fsk_filters.h b/fsk_filters.h
new file mode 100755
index 0000000..0786462
--- /dev/null
+++ b/fsk_filters.h
@@ -0,0 +1,7 @@
+/* 1200 bits/sec with Audio sample rate = 11025 */
+/* Mark freq = 1200, Space freq = 2200 */
+
+static const signed short m_sin_table[9] = { 0 , 7347 , 11257 , 9899 , 3909 , -3909 , -9899 , -11257 , -7347  };
+static const signed short m_cos_table[9] = { 11431 , 8756 , 1984 , -5715 , -10741 , -10741 , -5715 , 1984 , 8756  };
+static const signed short s_sin_table[9] = { 0 , 10950 , 6281 , -7347 , -10496 , 1327 , 11257 , 5130 , -8314  };
+static const signed short s_cos_table[9] = { 11431 , 3278 , -9550 , -8756 , 4527 , 11353 , 1984 , -10215 , -7844  };
diff --git a/fsk_gen_filter.h b/fsk_gen_filter.h
new file mode 100755
index 0000000..608c69b
--- /dev/null
+++ b/fsk_gen_filter.h
@@ -0,0 +1,15 @@
+
+
+#ifndef FSK_GEN_FILTER_H
+#define FSK_GEN_FILTER_H 1
+
+#include "audio.h"
+#include "fsk_demod_state.h"
+
+void fsk_gen_filter (int samples_per_sec, 
+			int baud, 
+			int mark_freq, int space_freq, 
+			char profile,
+			struct demodulator_state_s *D);
+
+#endif
\ No newline at end of file
diff --git a/gen_packets.c b/gen_packets.c
new file mode 100755
index 0000000..28014c4
--- /dev/null
+++ b/gen_packets.c
@@ -0,0 +1,729 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:	gen_packets.c
+ *
+ * Purpose:	Test program for generating AFSK AX.25 frames.
+ *
+ * Description:	Given messages are converted to audio and written 
+ *		to a .WAV type audio file.
+ *
+ *
+ * Bugs:	Most options not implemented for second audio channel.
+ *
+ *------------------------------------------------------------------*/
+
+
+
+
+#include <stdio.h>     
+#include <stdlib.h>    
+#include <getopt.h>
+#include <string.h>
+#include <assert.h>
+
+#include "audio.h"
+#include "ax25_pad.h"
+#include "hdlc_send.h"
+#include "gen_tone.h"
+#include "textcolor.h"
+
+
+static void usage (char **argv);
+static int audio_file_open (char *fname, struct audio_s *pa);
+static int audio_file_close (void);
+
+static int g_add_noise = 0;
+static float g_noise_level = 0;
+
+
+
+int main(int argc, char **argv)
+{
+    int c;
+    int digit_optind = 0;
+    int err;
+    unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
+    int flen;
+    int packet_count = 0;
+    int i;
+    int chan;
+
+/*
+ * Set up default values for the modem.
+ */
+        struct audio_s modem;
+
+        modem.num_channels = DEFAULT_NUM_CHANNELS;              /* -2 stereo */
+        modem.samples_per_sec = DEFAULT_SAMPLES_PER_SEC;        /* -r option */
+        modem.bits_per_sample = DEFAULT_BITS_PER_SAMPLE;        /* -8 for 8 instead of 16 bits */
+        
+	for (chan = 0; chan < MAX_CHANS; chan++) {
+	  modem.modem_type[chan] = AFSK;				/* change with -g */
+	  modem.mark_freq[chan] = DEFAULT_MARK_FREQ;                    /* -m option */
+          modem.space_freq[chan] = DEFAULT_SPACE_FREQ;                  /* -s option */
+          modem.baud[chan] = DEFAULT_BAUD;                              /* -b option */
+	}
+
+/*
+ * Set up other default values.
+ */
+    int amplitude = 50;			/* -a option */
+    int leading_zeros = 12;		/* -z option */
+    char output_file[256];		/* -o option */
+    FILE *input_fp = NULL;		/* File or NULL for built-in message */
+
+    packet_t pp;
+
+
+    strcpy (output_file, "");
+
+
+    while (1) {
+        int this_option_optind = optind ? optind : 1;
+        int option_index = 0;
+        static struct option long_options[] = {
+            {"future1", 1, 0, 0},
+            {"future2", 0, 0, 0},
+            {"future3", 1, 0, 'c'},
+            {0, 0, 0, 0}
+        };
+
+	/* ':' following option character means arg is required. */
+
+        c = getopt_long(argc, argv, "gm:s:a:b:B:r:n:o:z:82",
+                        long_options, &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+
+          case 0:				/* possible future use */
+
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf("option %s", long_options[option_index].name);
+            if (optarg) {
+                dw_printf(" with arg %s", optarg);
+            }
+            dw_printf("\n");
+            break;
+
+          case 'b':				/* -b for data Bit rate */
+
+            modem.baud[0] = atoi(optarg);
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf ("Data rate set to %d bits / second.\n", modem.baud[0]);
+            if (modem.baud[0] < 100 || modem.baud[0] > 10000) {
+              text_color_set(DW_COLOR_ERROR); 
+              dw_printf ("Use a more reasonable bit rate in range of 100 - 10000.\n");
+              exit (EXIT_FAILURE);
+            }
+            break;
+
+          case 'B':				/* -B for data Bit rate */
+						/*    300 implies 1600/1800 AFSK. */
+						/*    1200 implies 1200/2200 AFSK. */
+						/*    9600 implies scrambled. */
+
+            modem.baud[0] = atoi(optarg);
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf ("Data rate set to %d bits / second.\n", modem.baud[0]);
+            if (modem.baud[0] < 100 || modem.baud[0] > 10000) {
+              text_color_set(DW_COLOR_ERROR); 
+              dw_printf ("Use a more reasonable bit rate in range of 100 - 10000.\n");
+              exit (EXIT_FAILURE);
+            }
+
+	    switch (modem.baud[0]) {
+	      case 300:
+                modem.mark_freq[0] = 1600;
+                modem.space_freq[0] = 1800;
+	        break;
+	      case 1200:
+                modem.mark_freq[0] = 1200;
+                modem.space_freq[0] = 2200;
+	        break;
+	      case 9600:
+                modem.modem_type[0] = SCRAMBLE;
+                text_color_set(DW_COLOR_INFO); 
+                dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
+	        break;
+	    }
+            break;
+
+          case 'g':				/* -g for g3ruh scrambling */
+
+            modem.modem_type[0] = SCRAMBLE;
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
+            break;
+
+          case 'm':				/* -m for Mark freq */
+
+            modem.mark_freq[0] = atoi(optarg);
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf ("Mark frequency set to %d Hz.\n", modem.mark_freq[0]);
+            if (modem.mark_freq[0] < 300 || modem.mark_freq[0] > 3000) {
+              text_color_set(DW_COLOR_ERROR); 
+	      dw_printf ("Use a more reasonable value in range of 300 - 3000.\n");
+              exit (EXIT_FAILURE);
+            }
+            break;
+
+          case 's':				/* -s for Space freq */
+
+            modem.space_freq[0] = atoi(optarg);
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf ("Space frequency set to %d Hz.\n", modem.space_freq[0]);
+            if (modem.space_freq[0] < 300 || modem.space_freq[0] > 3000) {
+              text_color_set(DW_COLOR_ERROR); 
+	      dw_printf ("Use a more reasonable value in range of 300 - 3000.\n");
+              exit (EXIT_FAILURE);
+            }
+            break;
+
+          case 'n':				/* -n number of packets with increasing noise. */
+
+	    packet_count = atoi(optarg);
+
+	    g_add_noise = 1;
+
+            break;
+
+          case 'a':				/* -a for amplitude */
+
+            amplitude = atoi(optarg);
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf ("Amplitude set to %d%%.\n", amplitude);
+            if (amplitude < 0 || amplitude > 100) {
+              text_color_set(DW_COLOR_ERROR); 
+	      dw_printf ("Amplitude must be in range of 0 to 100.\n");
+              exit (EXIT_FAILURE);
+            }
+            break;
+
+          case 'r':				/* -r for audio sample Rate */
+
+            modem.samples_per_sec = atoi(optarg);
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf ("Audio sample rate set to %d samples / second.\n", modem.samples_per_sec);
+            if (modem.samples_per_sec < MIN_SAMPLES_PER_SEC || modem.samples_per_sec > MAX_SAMPLES_PER_SEC) {
+              text_color_set(DW_COLOR_ERROR); 
+	      dw_printf ("Use a more reasonable audio sample rate in range of %d - %d.\n",
+						MIN_SAMPLES_PER_SEC, MAX_SAMPLES_PER_SEC);
+              exit (EXIT_FAILURE);
+            }
+            break;
+
+          case 'z':				/* -z leading zeros before frame flag */
+
+            leading_zeros = atoi(optarg);
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf ("Send %d zero bits before frame flag.\n", leading_zeros);
+
+	    /* The demodulator needs a few for the clock recovery PLL. */
+	    /* We don't want to be here all day either. */
+            /* We can't translast to time yet because the data bit rate */
+            /* could be changed later. */
+
+            if (leading_zeros < 8 || leading_zeros > 12000) {
+              text_color_set(DW_COLOR_ERROR); 
+	      dw_printf ("Use a more reasonable value.\n");
+              exit (EXIT_FAILURE);
+            }
+            break;
+
+          case '8':				/* -8 for 8 bit samples */
+
+            modem.bits_per_sample = 8;
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf("8 bits per audio sample rather than 16.\n");
+            break;
+
+          case '2':				/* -2 for 2 channels of sound */
+
+            modem.num_channels = 2;
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf("2 channels of sound rather than 1.\n");
+            break;
+
+          case 'o':				/* -o for Output file */
+
+            strcpy (output_file, optarg);
+            text_color_set(DW_COLOR_INFO); 
+            dw_printf ("Output file set to %s\n", output_file);
+            break;
+
+          case '?':
+
+            /* Unknown option message was already printed. */
+            usage (argv);
+            break;
+
+          default:
+
+            /* Should not be here. */
+            text_color_set(DW_COLOR_ERROR); 
+            dw_printf("?? getopt returned character code 0%o ??\n", c);
+            usage (argv);
+        }
+    }
+
+    if (optind < argc) {
+
+	char str[400];
+
+        // dw_printf("non-option ARGV-elements: ");
+        // while (optind < argc)
+            // dw_printf("%s ", argv[optind++]);
+        //dw_printf("\n");
+
+        if (optind < argc - 1) {
+          text_color_set(DW_COLOR_ERROR); 
+	  dw_printf ("Warning: File(s) beyond the first are ignored.\n");
+        }
+
+        if (strcmp(argv[optind], "-") == 0) {
+          text_color_set(DW_COLOR_INFO); 
+          dw_printf ("Reading from stdin ...\n");
+          input_fp = stdin;
+        }
+        else {
+          input_fp = fopen(argv[optind], "r");
+          if (input_fp == NULL) {
+            text_color_set(DW_COLOR_ERROR); 
+ 	    dw_printf ("Can't open %s for read.\n", argv[optind]);
+            exit (EXIT_FAILURE);
+          }
+          text_color_set(DW_COLOR_INFO); 
+          dw_printf ("Reading from %s ...\n", argv[optind]);    
+        }
+
+        while (fgets (str, sizeof(str), input_fp) != NULL) {
+          text_color_set(DW_COLOR_REC); 
+          dw_printf ("%s", str);
+	}
+
+        if (input_fp != stdin) {
+          fclose (input_fp);
+        }
+    }
+    else {
+      text_color_set(DW_COLOR_INFO); 
+      dw_printf ("built in message...\n");
+    }
+
+
+        if (strlen(output_file) == 0) {
+          text_color_set(DW_COLOR_ERROR); 
+          dw_printf ("ERROR: The -o ouput file option must be specified.\n");
+          usage (argv);
+          exit (1);
+        }
+
+	err = audio_file_open (output_file, &modem);
+
+
+        if (err < 0) {
+          text_color_set(DW_COLOR_ERROR); 
+          dw_printf ("ERROR - Can't open output file.\n");
+          exit (1);
+        }
+
+
+	gen_tone_init (&modem, amplitude);
+
+        assert (modem.bits_per_sample == 8 || modem.bits_per_sample == 16);
+        assert (modem.num_channels == 1 || modem.num_channels == 2);
+        assert (modem.samples_per_sec >= MIN_SAMPLES_PER_SEC && modem.samples_per_sec <= MAX_SAMPLES_PER_SEC);
+
+
+	if (packet_count > 0)  {
+
+/*
+ * Generate packets with increasing noise level.
+ * Would probably be better to record real noise from a radio but
+ * for now just use a random number generator.
+ */
+	  for (i = 1; i <= packet_count; i++) {
+
+	    char stemp[80];
+	
+	    if (modem.modem_type[0] == SCRAMBLE) {
+	      g_noise_level = 0.33 * (amplitude / 100.0) * ((float)i / packet_count);
+	    }
+	    else {
+	      g_noise_level = (amplitude / 100.0) * ((float)i / packet_count);
+	    }
+
+	    sprintf (stemp, "WB2OSZ-1>APRS,W1AB-9,W1ABC-10,WB1ABC-15:,Hello, world!  %04d", i);
+
+	    pp = ax25_from_text (stemp, 1);
+	    flen = ax25_pack (pp, fbuf);
+	    for (c=0; c<modem.num_channels; c++)
+	    {
+	      hdlc_send_flags (c, 8, 0);
+	      hdlc_send_frame (c, fbuf, flen);
+	      hdlc_send_flags (c, 2, 1);
+	    }
+	    ax25_delete (pp);
+	  }
+	}
+	else {
+
+/*
+ * Builtin default 4 packets.
+ */
+	  pp = ax25_from_text ("WB2OSZ-1>APRS,W1AB-9,W1ABC-10,WB1ABC-15:,Hello, world!", 1);
+	  flen = ax25_pack (pp, fbuf);
+	  for (c=0; c<modem.num_channels; c++)
+	  {
+	      hdlc_send_flags (c, 8, 0);
+	      hdlc_send_frame (c, fbuf, flen);
+	      hdlc_send_flags (c, 2, 1);
+	  }
+	  ax25_delete (pp);
+
+	  hdlc_send_flags (c, 8, 0);
+	
+	  pp = ax25_from_text ("WB2OSZ-1>APRS,W1AB-9*,W1ABC-10,WB1ABC-15:,Hello, world!", 1);
+	  flen = ax25_pack (pp, fbuf);
+	  for (c=0; c<modem.num_channels; c++)
+	  {
+	    hdlc_send_frame (c, fbuf, flen);
+	  }
+	  ax25_delete (pp);
+
+	  pp = ax25_from_text ("WB2OSZ-1>APRS,W1AB-9,W1ABC-10*,WB1ABC-15:,Hello, world!", 1);
+	  flen = ax25_pack (pp, fbuf);
+	  for (c=0; c<modem.num_channels; c++)
+	  {
+	    hdlc_send_frame (c, fbuf, flen);
+	  }
+	  ax25_delete (pp);
+
+	  pp = ax25_from_text ("WB2OSZ-1>APRS,W1AB-9,W1ABC-10,WB1ABC-15*:,Hello, world!", 1);
+	  flen = ax25_pack (pp, fbuf);
+	  for (c=0; c<modem.num_channels; c++)
+	  {
+	    hdlc_send_frame (c, fbuf, flen);
+	  }
+	  ax25_delete (pp);
+
+	  hdlc_send_flags (c, 2, 1);
+
+	}
+
+	audio_file_close();
+
+    	return EXIT_SUCCESS;
+}
+
+
+static void usage (char **argv)
+{
+
+	text_color_set(DW_COLOR_ERROR); 
+	dw_printf ("\n");
+	dw_printf ("Usage: xxx [options] [file]\n");
+	dw_printf ("Options:\n");
+	dw_printf ("  -a <number>   Signal amplitude in range of 0 - 100%%.  Default 50.\n");
+	dw_printf ("  -b <number>   Bits / second for data.  Default is %d.\n", DEFAULT_BAUD);
+	dw_printf ("  -B <number>   Bits / second for data.  Proper modem selected for 300, 1200, 9600.\n");
+	dw_printf ("  -g            Scrambled baseband rather than AFSK.\n");
+	dw_printf ("  -m <number>   Mark frequency.  Default is %d.\n", DEFAULT_MARK_FREQ);
+	dw_printf ("  -s <number>   Space frequency.  Default is %d.\n", DEFAULT_SPACE_FREQ);
+	dw_printf ("  -r <number>   Audio sample Rate.  Default is %d.\n", DEFAULT_SAMPLES_PER_SEC);
+	dw_printf ("  -n <number>   Generate specified number of frames with increasing noise.\n");
+	dw_printf ("  -o <file>     Send output to .wav file.\n");
+	dw_printf ("  -8            8 bit audio rather than 16.\n");
+	dw_printf ("  -2            2 channels of audio rather than 1.\n");
+	dw_printf ("  -z <number>   Number of leading zero bits before frame.\n");
+	dw_printf ("                  Default is 12 which is .01 seconds at 1200 bits/sec.\n");
+
+	dw_printf ("\n");
+	dw_printf ("An optional file may be specified to provide messages other than\n");
+	dw_printf ("the default built-n message. The format should correspond to ...\n");
+	dw_printf ("blah blah blah.  For example,\n");
+	dw_printf ("    WB2OSZ-1>APDW10,WIDE2-2:!4237.14NS07120.83W#\n");
+	dw_printf ("\n");
+	dw_printf ("Example:  %s\n", argv[0]);
+	dw_printf ("\n");
+        dw_printf ("    With all defaults, a built-in test message is generated\n");
+	dw_printf ("    with standard Bell 202 tones used for packet radio on ordinary\n");
+	dw_printf ("    VHF FM transceivers.\n");
+	dw_printf ("\n");
+	dw_printf ("Example:  %s -g -b 9600\n", argv[0]);
+	dw_printf ("Shortcut: %s -B 9600\n", argv[0]);
+	dw_printf ("\n");
+        dw_printf ("    9600 baud mode.\n");
+	dw_printf ("\n");
+	dw_printf ("Example:  %s -m 1600 -s 1800 -b 300\n", argv[0]);
+	dw_printf ("Shortcut: %s -B 300\n", argv[0]);
+	dw_printf ("\n");
+        dw_printf ("    200 Hz shift, 300 baud, suitable for HF SSB transceiver.\n");
+	dw_printf ("\n");
+	dw_printf ("Example:  echo -n \"WB2OSZ>WORLD:Hello, world!\" | %s -a 25 -o x.wav -\n", argv[0]);
+	dw_printf ("\n");
+        dw_printf ("    Read message from stdin and put quarter volume sound into the file x.wav.\n");
+
+	exit (EXIT_FAILURE);
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_file_open
+ *
+ * Purpose:     Open a .WAV format file for output.
+ *
+ * Inputs:      fname		- Name of .WAV file to create.
+ *
+ *		pa		- Address of structure of type audio_s.
+ *				
+ *				The fields that we care about are:
+ *					num_channels
+ *					samples_per_sec
+ *					bits_per_sample
+ *				If zero, reasonable defaults will be provided.
+ *         
+ * Returns:     0 for success, -1 for failure.
+ *		
+ *----------------------------------------------------------------*/
+
+struct wav_header {             /* .WAV file header. */
+        char riff[4];           /* "RIFF" */
+        int filesize;          /* file length - 8 */
+        char wave[4];           /* "WAVE" */
+        char fmt[4];            /* "fmt " */
+        int fmtsize;           /* 16. */
+        short wformattag;       /* 1 for PCM. */
+        short nchannels;        /* 1 for mono, 2 for stereo. */
+        int nsamplespersec;    /* sampling freq, Hz. */
+        int navgbytespersec;   /* = nblockalign * nsamplespersec. */
+        short nblockalign;      /* = wbitspersample / 8 * nchannels. */
+        short wbitspersample;   /* 16 or 8. */
+        char data[4];           /* "data" */
+        int datasize;          /* number of bytes following. */
+} ;
+
+				/* 8 bit samples are unsigned bytes */
+				/* in range of 0 .. 255. */
+ 
+				/* 16 bit samples are signed short */
+				/* in range of -32768 .. +32767. */
+
+static FILE *out_fp = NULL;
+
+static struct wav_header header;
+
+static int byte_count;			/* Number of data bytes written to file. */
+					/* Will be written to header when file is closed. */
+
+
+static int audio_file_open (char *fname, struct audio_s *pa)
+{
+	int n;
+
+/*
+ * Fill in defaults for any missing values.
+ */
+	if (pa -> num_channels == 0)
+	  pa -> num_channels = DEFAULT_NUM_CHANNELS;
+
+	if (pa -> samples_per_sec == 0)
+	  pa -> samples_per_sec = DEFAULT_SAMPLES_PER_SEC;
+
+	if (pa -> bits_per_sample == 0)
+	  pa -> bits_per_sample = DEFAULT_BITS_PER_SAMPLE;
+
+
+/*
+ * Write the file header.  Don't know length yet.
+ */
+        out_fp = fopen (fname, "wb");	
+	
+        if (out_fp == NULL) {
+           text_color_set(DW_COLOR_ERROR); dw_printf ("Couldn't open file for write: %s\n", fname);
+	   perror ("");
+           return (-1);
+        }
+
+	memset (&header, 0, sizeof(header));
+
+        memcpy (header.riff, "RIFF", (size_t)4);
+        header.filesize = 0; 
+        memcpy (header.wave, "WAVE", (size_t)4);
+        memcpy (header.fmt, "fmt ", (size_t)4);
+        header.fmtsize = 16;			// Always 16.
+        header.wformattag = 1;     		// 1 for PCM.
+
+        header.nchannels = pa -> num_channels;   		
+        header.nsamplespersec = pa -> samples_per_sec;    
+        header.wbitspersample = pa -> bits_per_sample;  
+		
+        header.nblockalign = header.wbitspersample / 8 * header.nchannels;     
+        header.navgbytespersec = header.nblockalign * header.nsamplespersec;   
+        memcpy (header.data, "data", (size_t)4);
+        header.datasize = 0;        
+
+	assert (header.nchannels == 1 || header.nchannels == 2);
+
+        n = fwrite (&header, sizeof(header), (size_t)1, out_fp);
+
+	if (n != 1) {
+          text_color_set(DW_COLOR_ERROR); 
+	  dw_printf ("Couldn't write header to: %s\n", fname);
+	  perror ("");
+	  fclose (out_fp);
+	  out_fp = NULL;
+          return (-1);
+        }
+
+
+/*
+ * Number of bytes written will be filled in later.
+ */
+        byte_count = 0;
+	
+	return (0);
+
+} /* end audio_open */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_put
+ *
+ * Purpose:     Send one byte to the audio output file.
+ *
+ * Inputs:	c	- One byte in range of 0 - 255.
+ *
+ * Returns:     Normally non-negative.
+ *              -1 for any type of error.
+ *
+ * Description:	The caller must deal with the details of mono/stereo
+ *		and number of bytes per sample.
+ *
+ *----------------------------------------------------------------*/
+
+
+int audio_put (int c)
+{
+	static short sample16;
+	int s;
+
+	if (g_add_noise) {
+
+	  if ((byte_count & 1) == 0) {
+	    sample16 = c & 0xff;		/* save lower byte. */
+	    byte_count++;
+	    return c;
+	  }
+	  else {
+	    float r;
+
+	    sample16 |= (c << 8) & 0xff00;	/* insert upper byte. */
+	    byte_count++;
+	    s = sample16;  // sign extend.
+
+/* Add random noise to the signal. */
+/* r should be in range of -1 .. +1. */
+	    
+	    r = (rand() - RAND_MAX/2.0) / (RAND_MAX/2.0);
+
+	    s += 5 * r * g_noise_level * 32767;
+
+	    if (s > 32767) s = 32767;
+	    if (s < -32767) s = -32767;
+
+	    putc(s & 0xff, out_fp);  
+	    return (putc((s >> 8) & 0xff, out_fp));
+	  }
+	}
+	else {
+	  byte_count++;
+	  return (putc(c, out_fp));
+	}
+
+} /* end audio_put */
+
+
+int audio_flush ()
+{
+	return 0;
+}
+
+/*------------------------------------------------------------------
+ *
+ * Name:        audio_file_close
+ *
+ * Purpose:     Close the audio output file.
+ *
+ * Returns:     Normally non-negative.
+ *              -1 for any type of error.
+ *
+ *
+ * Description:	Must go back to beginning of file and fill in the
+ *		size of the data.
+ *
+ *----------------------------------------------------------------*/
+
+static int audio_file_close (void)
+{
+	int n;
+
+        text_color_set(DW_COLOR_DEBUG); 
+	dw_printf ("audio_close()\n");
+
+/*
+ * Go back and fix up lengths in header.
+ */
+        header.filesize = byte_count + sizeof(header) - 8;           
+        header.datasize = byte_count;        
+
+	if (out_fp == NULL) {
+	  return (-1);
+ 	}
+
+        fflush (out_fp);
+
+        fseek (out_fp, 0L, SEEK_SET);         
+        n = fwrite (&header, sizeof(header), (size_t)1, out_fp);
+
+	if (n != 1) {
+          text_color_set(DW_COLOR_ERROR); 
+	  dw_printf ("Couldn't write header to audio file.\n");
+	  perror ("");		// TODO: remove perror.
+	  fclose (out_fp);
+	  out_fp = NULL;
+          return (-1);
+        }
+
+        fclose (out_fp);
+        out_fp = NULL;
+
+	return (0);
+
+} /* end audio_close */
+
diff --git a/gen_tone.c b/gen_tone.c
new file mode 100755
index 0000000..39b3bf0
--- /dev/null
+++ b/gen_tone.c
@@ -0,0 +1,334 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      gen_tone.c
+ *
+ * Purpose:     Convert bits to AFSK for writing to .WAV sound file 
+ *		or a sound device.
+ *
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <math.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "direwolf.h"
+#include "audio.h"
+#include "gen_tone.h"
+#include "textcolor.h"
+
+
+
+// Properties of the digitized sound stream & modem.
+
+static struct audio_s modem;
+
+/*
+ * 8 bit samples are unsigned bytes in range of 0 .. 255.
+ *
+ * 16 bit samples are signed short in range of -32768 .. +32767.
+ */
+
+
+/* Constants after initialization. */
+
+#define TICKS_PER_CYCLE ( 256.0 * 256.0 * 256.0 * 256.0 )
+
+static int ticks_per_sample;		/* same for all channels. */
+
+static int ticks_per_bit[MAX_CHANS];
+static int f1_change_per_sample[MAX_CHANS];
+static int f2_change_per_sample[MAX_CHANS];
+
+static short sine_table[256];
+
+
+/* Accumulators. */
+
+static unsigned int tone_phase[MAX_CHANS]; // Phase accumulator for tone generation.
+					    // Upper bits are used as index into sine table.
+
+static int bit_len_acc[MAX_CHANS];	// To accumulate fractional samples per bit.
+
+static int lfsr[MAX_CHANS];		// Shift register for scrambler.
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        gen_tone_init
+ *
+ * Purpose:     Initialize for AFSK tone generation which might
+ *		be used for RTTY or amateur packet radio.
+ *
+ * Inputs:      pp		- Pointer to modem parameter structure, modem_s.
+ *
+ *				The fields we care about are:
+ *
+ *					samples_per_sec
+ *					baud
+ *					mark_freq
+ *					space_freq
+ *					samples_per_sec
+ *
+ *		amp		- Signal amplitude on scale of 0 .. 100.
+ *
+ * Returns:     0 for success.
+ *              -1 for failure.
+ *
+ * Description:	 Calculate various constants for use by the direct digital synthesis
+ * 		audio tone generation.
+ *
+ *----------------------------------------------------------------*/
+
+static int amp16bit;	/* for 9600 baud */
+
+
+int gen_tone_init (struct audio_s *pp, int amp)  
+{
+	int j;
+	int chan = 0;
+/* 
+ * Save away modem parameters for later use. 
+ */
+
+	memcpy (&modem, pp, sizeof(struct audio_s));
+
+	amp16bit = (32767 * amp) / 100;
+
+	ticks_per_sample = (int) ((TICKS_PER_CYCLE / (double)modem.samples_per_sec ) + 0.5);
+
+	for (chan = 0; chan < modem.num_channels; chan++) {
+
+	  ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)modem.baud[chan] ) + 0.5);
+
+	  f1_change_per_sample[chan] = (int) (((double)modem.mark_freq[chan] * TICKS_PER_CYCLE / (double)modem.samples_per_sec ) + 0.5);
+
+	  f2_change_per_sample[chan] = (int) (((double)modem.space_freq[chan] * TICKS_PER_CYCLE / (double)modem.samples_per_sec ) + 0.5);
+
+	  tone_phase[chan] = 0;
+				
+	  bit_len_acc[chan] = 0;
+
+	  lfsr[chan] = 0;
+	}
+
+        for (j=0; j<256; j++) {
+	  double a;
+	  int s;
+
+	  a = ((double)(j) / 256.0) * (2 * M_PI);
+	  s = (int) (sin(a) * 32767 * amp / 100.0);
+
+	  /* 16 bit sound sample is in range of -32768 .. +32767. */
+	  
+	  assert (s >= -32768 && s <= 32767);
+	
+	  sine_table[j] = s;
+        }
+
+	return (0);
+
+ } /* end gen_tone_init */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        gen_tone_put_bit
+ *
+ * Purpose:     Generate tone of proper duration for one data bit.
+ *
+ * Inputs:      chan	- Audio channel, 0 = first.
+ *
+ *		dat	- 0 for f1, 1 for f2.
+ *
+ * 			  	-1 inserts half bit to test data	
+ *				recovery PLL.
+ *
+ * Assumption:  fp is open to a file for write.
+ *
+ *--------------------------------------------------------------------*/
+
+void tone_gen_put_bit (int chan, int dat)
+{
+	int cps = dat ? f2_change_per_sample[chan] : f1_change_per_sample[chan];
+	unsigned short sam = 0;
+	int x;
+
+
+        if (dat < 0) { 
+	  /* Hack to test receive PLL recovery. */
+	  bit_len_acc[chan] -= ticks_per_bit[chan]; 
+	  dat = 0; 
+	} 
+
+	if (modem.modem_type[chan] == SCRAMBLE) {
+	  x = (dat ^ (lfsr[chan] >> 16) ^ (lfsr[chan] >> 11)) & 1;
+	  lfsr[chan] = (lfsr[chan] << 1) | (x & 1);
+	  dat = x;
+	}
+	  
+	do {
+
+	  if (modem.modem_type[chan] == AFSK) {
+	    tone_phase[chan] += cps;
+            sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
+	  }
+  	  else {
+	    // TODO: Might want to low pass filter this.
+	    sam = dat ? amp16bit : (-amp16bit);
+	  }
+
+          /* Ship out an audio sample. */
+
+	  assert (modem.num_channels == 1 || modem.num_channels == 2);
+
+	  /* Generalize to allow 8 bits someday? */
+
+	  assert (modem.bits_per_sample == 16);
+
+
+	  if (modem.num_channels == 1)
+	  {
+            audio_put (sam & 0xff);
+            audio_put ((sam >> 8) & 0xff);
+ 	  }
+	  else if (modem.num_channels == 2)
+	  {
+	    if (chan == 1)
+	    {
+              audio_put (0);		// silent left
+              audio_put (0);
+	    }
+
+            audio_put (sam & 0xff);
+            audio_put ((sam >> 8) & 0xff);
+            //byte_count += 2;
+
+	    if (chan == 0)
+	    {
+              audio_put (0);		// silent right
+              audio_put (0);
+	    }
+	  }
+
+	  /* Enough for the bit time? */
+
+	  bit_len_acc[chan] += ticks_per_sample;
+
+        } while (bit_len_acc[chan] < ticks_per_bit[chan]);
+
+	bit_len_acc[chan] -= ticks_per_bit[chan];
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        main
+ *
+ * Purpose:     Quick test program for above.
+ *
+ * Description: Compile like this for unit test:
+ *
+ *		gcc -Wall -DMAIN -o gen_tone_test gen_tone.c audio.c textcolor.c
+ *
+ *		gcc -Wall -DMAIN -o gen_tone_test.exe gen_tone.c audio_win.c textcolor.c -lwinmm
+ *
+ *--------------------------------------------------------------------*/
+
+
+#if MAIN
+
+
+int main ()
+{
+	int n;
+	int chan1 = 0;
+	int chan2 = 1;
+	int r;
+	struct audio_s audio_param;
+
+
+/* to sound card */
+/* one channel.  2 times:  one second of each tone. */
+
+	memset (&audio_param, 0, sizeof(audio_param));
+	strcpy (audio_param.adevice_in, DEFAULT_ADEVICE);
+	strcpy (audio_param.adevice_out, DEFAULT_ADEVICE);
+
+	audio_open (&audio_param);
+	gen_tone_init (&audio_param, 100);
+
+	for (r=0; r<2; r++) {
+
+	  for (n=0; n<audio_param.baud[0] * 2 ; n++) {
+ 	    tone_gen_put_bit ( chan1, 1 );
+	  }
+
+	  for (n=0; n<audio_param.baud[0] * 2 ; n++) {
+ 	    tone_gen_put_bit ( chan1, 0 );
+	  }
+	}
+
+	audio_close();
+
+/* Now try stereo. */
+
+	memset (&audio_param, 0, sizeof(audio_param));
+	strcpy (audio_param.adevice_in, DEFAULT_ADEVICE);
+	strcpy (audio_param.adevice_out, DEFAULT_ADEVICE);
+	audio_param.num_channels = 2;
+
+	audio_open (&audio_param);
+	gen_tone_init (&audio_param, 100);
+
+	for (r=0; r<4; r++) {
+
+	  for (n=0; n<audio_param.baud[0] * 2 ; n++) {
+ 	    tone_gen_put_bit ( chan1, 1 );
+	  }
+
+	  for (n=0; n<audio_param.baud[0] * 2 ; n++) {
+ 	    tone_gen_put_bit ( chan1, 0 );
+	  }
+
+	  for (n=0; n<audio_param.baud[0] * 2 ; n++) {
+ 	    tone_gen_put_bit ( chan2, 1 );
+	  }
+
+	  for (n=0; n<audio_param.baud[0] * 2 ; n++) {
+ 	    tone_gen_put_bit ( chan2, 0 );
+	  }
+	}
+
+	audio_close();
+
+	return(0);
+}
+
+#endif
+
+
+/* end gen_tone.c */
diff --git a/gen_tone.h b/gen_tone.h
new file mode 100755
index 0000000..b4b3f30
--- /dev/null
+++ b/gen_tone.h
@@ -0,0 +1,16 @@
+/*
+ * gen_tone.h
+ */
+
+
+int gen_tone_init (struct audio_s *pp, int amp);
+
+
+//int gen_tone_open (int nchan, int sample_rate, int bit_rate, int f1, int f2, int amp, char *fname);
+
+//int gen_tone_open_fd (int nchan, int sample_rate, int bit_rate, int f1, int f2, int amp, int fd)  ;
+
+//int gen_tone_close (void);
+
+void tone_gen_put_bit (int chan, int dat);
+
diff --git a/hdlc_rec.c b/hdlc_rec.c
new file mode 100755
index 0000000..0a5c7f3
--- /dev/null
+++ b/hdlc_rec.c
@@ -0,0 +1,513 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/********************************************************************************
+ *
+ * File:	hdlc_rec.c
+ *
+ * Purpose:	Extract HDLC frames from a stream of bits.
+ *
+ *******************************************************************************/
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "direwolf.h"
+#include "demod.h"
+#include "hdlc_rec.h"
+#include "hdlc_rec2.h"
+#include "fcs_calc.h"
+#include "textcolor.h"
+#include "ax25_pad.h"
+#include "rrbb.h"
+#include "multi_modem.h"
+
+
+//#define TEST 1				/* Define for unit testing. */
+
+//#define DEBUG3 1				/* monitor the data detect signal. */
+
+
+
+/* 
+ * Minimum & maximum sizes of an AX.25 frame including the 2 octet FCS. 
+ */
+
+#define MIN_FRAME_LEN ((AX25_MIN_PACKET_LEN) + 2)
+				
+#define MAX_FRAME_LEN ((AX25_MAX_PACKET_LEN) + 2)	
+
+/*
+ * This is the current state of the HDLC decoder.
+ *
+ * It is possible to run multiple decoders concurrently by
+ * having a separate set of state variables for each.
+ *
+ * Should have a reset function instead of initializations here.
+ */
+
+struct hdlc_state_s {
+
+	int prev_raw;			/* Keep track of previous bit so */
+					/* we can look for transitions. */
+					/* Should be only 0 or 1. */
+
+	unsigned char pat_det; 		/* 8 bit pattern detector shift register. */
+					/* See below for more details. */
+
+	unsigned int flag4_det;		/* Last 32 raw bits to look for 4 */
+					/* flag patterns in a row. */
+
+	unsigned char oacc;		/* Accumulator for building up an octet. */
+
+	int olen;			/* Number of bits in oacc. */
+					/* When this reaches 8, oacc is copied */
+					/* to the frame buffer and olen is zeroed. */
+					/* The value of -1 is a special case meaning */
+					/* bits should not be accumulated. */
+
+	unsigned char frame_buf[MAX_FRAME_LEN];
+					/* One frame is kept here. */
+
+	int frame_len;			/* Number of octets in frame_buf. */
+					/* Should be in range of 0 .. MAX_FRAME_LEN. */
+
+	int data_detect;		/* True when HDLC data is detected. */
+					/* This will not be triggered by voice or other */
+					/* noise or even tones.  */
+
+	enum retry_e fix_bits;		/* Level of effort to recover from */
+					/* a bad FCS on the frame. */
+
+	rrbb_t rrbb;			/* Handle for bit array for raw received bits. */
+					
+};
+
+
+static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS];
+
+static int num_subchan[MAX_CHANS];
+
+
+/***********************************************************************************
+ *
+ * Name:	hdlc_rec_init
+ *
+ * Purpose:	Call once at the beginning to initialize.
+ *
+ * Inputs:	None.
+ *
+ ***********************************************************************************/
+
+static int was_init = 0;
+
+void hdlc_rec_init (struct audio_s *pa)
+{
+	int j, k;
+	struct hdlc_state_s *H;
+
+	//text_color_set(DW_COLOR_DEBUG);
+	//dw_printf ("hdlc_rec_init (%p) \n", pa);
+
+	assert (pa != NULL);
+	
+	for (j=0; j<pa->num_channels; j++)
+	{
+	  num_subchan[j] = pa->num_subchan[j];
+
+	  assert (num_subchan[j] >= 1 && num_subchan[j] < MAX_SUBCHANS);
+
+	  for (k=0; k<MAX_SUBCHANS; k++) 
+	  {
+	    H = &hdlc_state[j][k];
+
+	    H->prev_raw = 0;
+	    H->pat_det = 0;
+	    H->flag4_det = 0;
+	    H->olen = -1;
+	    H->frame_len = 0;
+	    H->data_detect = 0;
+	    H->fix_bits = pa->fix_bits;
+	    H->rrbb = rrbb_new(j, k, pa->modem_type[j] == SCRAMBLE, -1);
+	  }
+	}
+
+	was_init = 1;
+}
+
+
+
+/***********************************************************************************
+ *
+ * Name:	hdlc_rec_bit
+ *
+ * Purpose:	Extract HDLC frames from a stream of bits.
+ *
+ * Inputs:	chan	- Channel number.  
+ *
+ *		subchan	- This allows multiple decoders per channel.
+ *
+ *		raw 	- One bit from the demodulator.
+ *			  should be 0 or 1.
+ *	
+ *		is_scrambled - Is the data scrambled?
+ *
+ *		descram_state - Current descrambler state.
+ *					
+ *		sam	- Possible future: Sample from demodulator output.
+ *			  Should nominally be in roughly -1 to +1 range.
+ *
+ * Description:	This is called once for each received bit.
+ *		For each valid frame, process_rec_frame()
+ *		is called for further processing.
+ *
+ ***********************************************************************************/
+
+#if SLICENDICE
+void hdlc_rec_bit_sam (int chan, int subchan, int raw, float demod_out)
+#else
+void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram_state)
+#endif
+{
+
+	int dbit;			/* Data bit after undoing NRZI. */
+					/* Should be only 0 or 1. */
+	struct hdlc_state_s *H;
+
+	assert (was_init == 1);
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+/*
+ * Different state information for each channel.
+ */
+	H = &hdlc_state[chan][subchan];
+
+/*
+ * Using NRZI encoding,
+ *   A '0' bit is represented by an inversion since previous bit.
+ *   A '1' bit is represented by no change.
+ */
+
+	dbit = (raw == H->prev_raw);
+	H->prev_raw = raw;
+
+/*
+ * Octets are sent LSB first.
+ * Shift the most recent 8 bits thru the pattern detector.
+ */
+	H->pat_det >>= 1;
+	if (dbit) {
+	  H->pat_det |= 0x80;
+	}
+
+	H->flag4_det >>= 1;
+	if (dbit) {
+	  H->flag4_det |= 0x80000000;
+	}
+
+
+/*
+ * "Data Carrier detect" function based on data rather than
+ * tones from a modem.
+ *
+ * Idle time, at beginning of transmission should be filled
+ * with the special "flag" characters.
+ *
+ * Idle time of all zero bits (alternating tones at maximum rate)
+ * has also been observed. 
+ */
+
+	if (H->flag4_det == 0x7e7e7e7e) {	
+	  
+
+	  if ( ! H->data_detect) {
+	    H->data_detect = 1;
+#if DEBUG3
+	    text_color_set(DW_COLOR_DEBUG);
+	    dw_printf ("DCD%d = 1 flags\n", chan);
+#endif
+	  }
+	}
+
+	if (H->flag4_det == 0x7e000000) {	
+	  
+
+	  if ( ! H->data_detect) {
+	    H->data_detect = 1;
+#if DEBUG3
+	    text_color_set(DW_COLOR_DEBUG);
+	    dw_printf ("DCD%d = 1 zero fill\n", chan);
+#endif
+	  }
+	}
+
+
+/* 
+ * Loss of signal should result in lack of transitions.
+ * (all '1' bits) for at least a little while.
+ */
+
+  
+	if (H->pat_det == 0xff) {	
+	  
+	  if ( H->data_detect ) {
+	    H->data_detect = 0;
+#if DEBUG3
+	    text_color_set(DW_COLOR_DEBUG);
+	    dw_printf ("DCD%d =   0\n", chan);
+#endif
+	  }
+	}
+
+
+/*
+ * End of data carrier detect.  
+ * 
+ * The rest is concerned with framing.
+ */
+
+#if SLICENDICE
+	rrbb2_append_bit (H->rrbb, demod_out);
+#else
+	rrbb_append_bit (H->rrbb, raw);
+#endif
+	if (H->pat_det == 0x7e) {
+
+	  rrbb_chop8 (H->rrbb);
+
+/*
+ * The special pattern 01111110 indicates beginning and ending of a frame.  
+ * If we have an adequate number of whole octets, it is a candidate for 
+ * further processing.
+ *
+ * It might look odd that olen is being tested for 7 instead of 0.
+ * This is because oacc would already have 7 bits from the special
+ * "flag" pattern before it is detected here.
+ */
+
+
+#if OLD_WAY
+
+#if TEST
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("\nfound flag, olen = %d, frame_len = %d\n", olen, frame_len);
+#endif
+	  if (H->olen == 7 && H->frame_len >= MIN_FRAME_LEN) {
+
+	    unsigned short actual_fcs, expected_fcs;
+
+#if TEST
+	    int j;
+	    dw_printf ("TRADITIONAL: frame len = %d\n", H->frame_len);
+	    for (j=0; j<H->frame_len; j++) {
+	      dw_printf ("  %02x", H->frame_buf[j]);
+	    }
+	    dw_printf ("\n");
+
+#endif
+	    /* Check FCS, low byte first, and process... */
+
+	    /* Alternatively, it is possible to include the two FCS bytes */
+	    /* in the CRC calculation and look for a magic constant.  */
+	    /* That would be easier in the case where the CRC is being */
+	    /* accumulated along the way as the octets are received. */
+	    /* I think making a second pass over it and comparing is */
+	    /* easier to understand. */
+
+	    actual_fcs = H->frame_buf[H->frame_len-2] | (H->frame_buf[H->frame_len-1] << 8);
+
+	    expected_fcs = fcs_calc (H->frame_buf, H->frame_len - 2);
+
+	    if (actual_fcs == expected_fcs) {
+	      int alevel = demod_get_audio_level (chan, subchan);
+
+	      multi_modem_process_rec_frame (chan, subchan, H->frame_buf, H->frame_len - 2, alevel, RETRY_NONE);   /* len-2 to remove FCS. */
+	    }
+	    else {
+
+#if TEST
+	      dw_printf ("*** actual fcs = %04x, expected fcs = %04x ***\n", actual_fcs, expected_fcs);
+#endif
+
+	    }
+
+	  }
+
+#else
+
+/*
+ * New way - Decode the raw bits in later step.
+ */
+
+#if TEST
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("\nfound flag, %d bits in frame\n", rrbb_get_len(H->rrbb) - 1);
+#endif
+	  if (rrbb_get_len(H->rrbb) >= MIN_FRAME_LEN * 8) {
+		
+	    int alevel = demod_get_audio_level (chan, subchan);
+
+	    rrbb_set_audio_level (H->rrbb, alevel);
+	    hdlc_rec2_block (H->rrbb, H->fix_bits);
+	    	/* Now owned by someone else who will free it. */
+	    H->rrbb = rrbb_new (chan, subchan, is_scrambled, descram_state); /* Allocate a new one. */
+	  }
+	  else {
+	    rrbb_clear (H->rrbb, is_scrambled, descram_state); 
+	  }
+
+	  H->olen = 0;		/* Allow accumulation of octets. */
+	  H->frame_len = 0;
+
+#if SLICENDICE
+	  rrbb2_append_bit (H->rrbb, H->prev_raw ? 1.0 : -1.0); /* Last bit of flag.  Needed to get first data bit. */
+#else
+	  rrbb_append_bit (H->rrbb, H->prev_raw); /* Last bit of flag.  Needed to get first data bit. */
+#endif
+#endif
+
+	}
+	else if (H->pat_det == 0xfe) {
+
+/*
+ * Valid data will never have 7 one bits in a row.
+ *
+ *	11111110
+ *
+ * This indicates loss of signal.
+ */
+
+	  H->olen = -1;		/* Stop accumulating octets. */
+	  H->frame_len = 0;	/* Discard anything in progress. */
+
+	  rrbb_clear (H->rrbb, is_scrambled, descram_state); 
+#if SLICENDICE
+	  rrbb2_append_bit (H->rrbb, H->prev_raw ? 1.0 : -1.0); /* Last bit of flag.  Needed to get first data bit. */
+#else
+	  rrbb_append_bit (H->rrbb, H->prev_raw); /* Last bit of flag.  Needed to get first data bit. */
+#endif
+	}
+	else if ( (H->pat_det & 0xfc) == 0x7c ) {
+
+/*
+ * If we have five '1' bits in a row, followed by a '0' bit,
+ *
+ *	0111110xx
+ *
+ * the current '0' bit should be discarded because it was added for 
+ * "bit stuffing."
+ */
+	  ;
+
+	} else {
+
+/*
+ * In all other cases, accumulate bits into octets, and complete octets
+ * into the frame buffer.
+ */
+	  if (H->olen >= 0) {
+
+	    H->oacc >>= 1;
+	    if (dbit) {
+	      H->oacc |= 0x80;
+	    }
+	    H->olen++;
+
+	    if (H->olen == 8) {
+	      H->olen = 0;
+
+	      if (H->frame_len < MAX_FRAME_LEN) {
+		H->frame_buf[H->frame_len] = H->oacc;
+		H->frame_len++;
+	      }
+	    }
+	  }
+	}
+}
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        hdlc_rec_data_detect_1
+ *		hdlc_rec_data_detect_any
+ *
+ * Purpose:     Determine if the radio channel is curently busy
+ *		with packet data.
+ *		This version doesn't care about voice or other sounds.
+ *		This is used by the transmit logic to transmit only
+ *		when the channel is clear.
+ *
+ * Inputs:	chan	- Audio channel.  0 for left, 1 for right.
+ *
+ * Returns:	True if channel is busy (data detected) or 
+ *		false if OK to transmit. 
+ *
+ *
+ * Description:	We have two different versions here.
+ *
+ *		hdlc_rec_data_detect_1 tests a single decoder (subchan)
+ *		and is used by the DPLL to determine how much inertia
+ *		to use when trying to follow the incoming signal.
+ *
+ *		hdlc_rec_data_detect_any sees if ANY of the decoders
+ *		for this channel are receving a signal.   This is
+ *		used to determine whether the channel is clear and
+ *		we can transmit.  This would apply to the 300 baud
+ *		HF SSB case where we have multiple decoders running
+ *		at the same time.  The channel is busy if ANY of them
+ *		thinks the channel is busy.
+ *
+ *--------------------------------------------------------------------*/
+
+int hdlc_rec_data_detect_1 (int chan, int subchan)
+{
+	assert (chan >= 0 && chan < MAX_CHANS);
+
+	return ( hdlc_state[chan][subchan].data_detect );
+
+} /* end hdlc_rec_data_detect_1 */
+
+
+int hdlc_rec_data_detect_any (int chan)
+{
+	int subchan;
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+
+	for (subchan = 0; subchan < num_subchan[chan]; subchan++) {
+
+	  assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+	  if (hdlc_state[chan][subchan].data_detect) {
+	    return (1);
+	  }
+	}
+	return (0);
+
+
+} /* end hdlc_rec_data_detect_any */
+
+
+/* end hdlc_rec.c */
+
+
diff --git a/hdlc_rec.h b/hdlc_rec.h
new file mode 100755
index 0000000..8ef86f0
--- /dev/null
+++ b/hdlc_rec.h
@@ -0,0 +1,24 @@
+
+
+#include "audio.h"
+
+#include "rrbb.h"		/* Possibly defines SLICENDICE. */
+
+
+void hdlc_rec_init (struct audio_s *pa);
+
+#if SLICENDICE
+void hdlc_rec_bit_sam (int chan, int subchan, int raw, float demod_out);
+#else
+void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram_state);
+#endif
+
+
+/* Provided elsewhere to process a complete frame. */
+
+//void process_rec_frame (int chan, unsigned char *fbuf, int flen, int level);
+
+/* Transmit needs to know when someone else is transmitting. */
+
+int hdlc_rec_data_detect_1 (int chan, int subchan);
+int hdlc_rec_data_detect_any (int chan);
diff --git a/hdlc_rec2.c b/hdlc_rec2.c
new file mode 100755
index 0000000..629fa31
--- /dev/null
+++ b/hdlc_rec2.c
@@ -0,0 +1,661 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011, 2012, 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/********************************************************************************
+ *
+ * File:	hdlc_rec2.c
+ *
+ * Purpose:	Extract HDLC frame from a block of bits after someone
+ *		else has done the work of pulling it out from between
+ *		the special "flag" sequences.
+ *
+ *******************************************************************************/
+
+#include <stdio.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "direwolf.h"
+#include "hdlc_rec2.h"
+#include "fcs_calc.h"
+#include "textcolor.h"
+#include "ax25_pad.h"
+#include "rrbb.h"
+#include "rdq.h"
+#include "multi_modem.h"
+
+
+/* 
+ * Minimum & maximum sizes of an AX.25 frame including the 2 octet FCS. 
+ */
+
+#define MIN_FRAME_LEN ((AX25_MIN_PACKET_LEN) + 2)
+				
+#define MAX_FRAME_LEN ((AX25_MAX_PACKET_LEN) + 2)	
+
+/*
+ * This is the current state of the HDLC decoder.
+ *
+ * It is possible to run multiple decoders concurrently by
+ * having a separate set of state variables for each.
+ *
+ * Should have a reset function instead of initializations here.
+ */
+
+struct hdlc_state_s {
+
+	int prev_raw;			/* Keep track of previous bit so */
+					/* we can look for transitions. */
+					/* Should be only 0 or 1. */
+
+	unsigned char pat_det; 		/* 8 bit pattern detector shift register. */
+					/* See below for more details. */
+
+	unsigned char oacc;		/* Accumulator for building up an octet. */
+
+	int olen;			/* Number of bits in oacc. */
+					/* When this reaches 8, oacc is copied */
+					/* to the frame buffer and olen is zeroed. */
+
+	unsigned char frame_buf[MAX_FRAME_LEN];
+					/* One frame is kept here. */
+
+	int frame_len;			/* Number of octets in frame_buf. */
+					/* Should be in range of 0 .. MAX_FRAME_LEN. */
+
+};
+
+
+
+
+static int try_decode (rrbb_t block, int chan, int subchan, int alevel, retry_t bits_flipped, int flip_a, int flip_b, int flip_c);
+static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int alevel, retry_t fix_bits);
+static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped);
+#if DEBUG
+static double dtime_now (void);
+#endif
+
+/***********************************************************************************
+ *
+ * Name:	hdlc_rec2_block
+ *
+ * Purpose:	Extract HDLC frame from a stream of bits.
+ *
+ * Inputs:	block 		- Handle for bit array.
+ *		fix_bits	- Level of effort to recover frames with bad FCS.
+ *
+ * Description:	The other (original) hdlc decoder took one bit at a time
+ *		right out of the demodulator.
+ *
+ *		This is different in that it processes a block of bits
+ *		previously extracted from between two "flag" patterns.
+ *
+ *		This allows us to try decoding the same received data more
+ *		than once.
+ *
+ * Bugs:	This does not work for 9600 baud, more accurately when
+ *		the transmitted bits are scrambled.
+ *
+ *		Currently we unscramble the bits as they come from the 
+ *		receiver.  Instead we need to save the original received
+ *		bits and apply the descrambling after flipping the bits.
+ *
+ ***********************************************************************************/
+
+
+void hdlc_rec2_block (rrbb_t block, retry_t fix_bits)
+{
+	int chan = rrbb_get_chan(block);
+	int subchan = rrbb_get_subchan(block);
+	int alevel = rrbb_get_audio_level(block);
+	int ok;
+	int n;
+
+#if DEBUGx
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("\n--- try to decode ---\n");
+#endif
+
+#if SLICENDICE
+/*
+ * Unfinished experiment.  Get back to this again someday.
+ * The demodulator output is (should be) roughly in the range of -1 to 1.
+ * Formerly we sliced it at 0 and saved only a single bit for the sample.
+ * Now we save the sample so we can try adjusting the slicing point.
+ *
+ * First time thru, set the slicing point to 0.
+ */
+
+	for (n = 0; n < 1 ; n++) {
+
+	  rrbb_set_slice_val (block, n);
+
+	  ok = try_decode (block, chan, subchan, alevel, RETRY_NONE, -1, -1, -1);
+
+	  if (ok) {
+//#if DEBUG
+	    text_color_set(DW_COLOR_INFO);
+	    dw_printf ("Got it with no errors. Slice val = %d \n", n);
+//#endif
+	    rrbb_delete (block);
+	    return;
+	  }
+	}
+	rrbb_set_slice_val (block, 0);
+
+#else /* not SLICENDICE */
+
+	ok = try_decode (block, chan, subchan, alevel, RETRY_NONE, -1, -1, -1);
+
+	if (ok) {
+#if DEBUG
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf ("Got it the first time.\n");
+#endif
+	 rrbb_delete (block);
+	 return;
+	}
+#endif
+	
+	if (try_to_fix_quick_now (block, chan, subchan, alevel, fix_bits)) {
+	  rrbb_delete (block);
+	  return;
+	}
+
+/*
+ * Put in queue for retrying later at lower priority.
+ */
+
+	if (fix_bits < RETRY_TWO_SEP) {
+	  rrbb_delete (block); 
+	  return;
+	}
+
+	rdq_append (block);
+
+}
+
+
+static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int alevel, retry_t fix_bits)
+{
+	int ok;
+	int len, i;
+
+
+	len = rrbb_get_len(block);
+
+/* 
+ * Try fixing one bit.   
+ */
+	if (fix_bits < RETRY_SINGLE) {
+	  return 0;
+	}
+
+	for (i=0; i<len; i++) {
+	  ok = try_decode (block, chan, subchan, alevel, RETRY_SINGLE, i, -1, -1);
+	  if (ok) {
+#if DEBUG
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("*** Success by flipping SINGLE bit %d of %d ***\n", i, len);
+#endif
+	    return 1;
+	  }
+	}
+
+/* 
+ * Try fixing two adjacent bits.  
+ */
+	if (fix_bits < RETRY_DOUBLE) {
+	  return 0;
+	}
+
+	for (i=0; i<len-1; i++) {
+	  ok = try_decode (block, chan, subchan, alevel, RETRY_DOUBLE, i, i+1, -1);
+	  if (ok) {
+#if DEBUG
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("*** Success by flipping DOUBLE bit %d of %d ***\n", i, len);
+#endif
+	    return 1;
+	  }
+	}
+
+/*
+ * Try fixing adjacent three bits.
+ */
+	if (fix_bits < RETRY_TRIPLE) {
+	  return 0;
+	}
+
+	len = rrbb_get_len(block);
+	for (i=0; i<len-2; i++) {
+	  ok = try_decode (block, chan, subchan, alevel, RETRY_TRIPLE, i, i+1, i+2);
+	  if (ok) {
+#if DEBUG
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("*** Success by flipping TRIPLE bit %d of %d ***\n", i, len);
+#endif
+	    return 1;
+	  }
+	}
+
+	return 0;
+}
+
+void hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int alevel)
+{
+	int ok;
+	int len, i;
+#if DEBUG
+	double tstart, tend;
+#endif
+
+	len = rrbb_get_len(block);
+
+/*
+ * Two  non-adjacent ("separated") single bits.
+ * It chews up a lot of CPU time.  Test takes 4 times longer to run.
+ *
+ * Ran up to 4.82 seconds for 1040 bits before giving up.
+ * Processing time is order N squared so time goes up rapidly with larger frames.
+ */
+
+#if DEBUG
+	tstart = dtime_now();
+#endif
+	len = rrbb_get_len(block);
+	for (i=0; i<len-2; i++) {
+	  int j;
+
+	  ok = 0;
+	  for (j=i+2; j<len; j++) {
+	    ok = try_decode (block, chan, subchan, alevel, RETRY_TWO_SEP, i, j, -1);  
+	    if (ok)
+	      break;
+	  }	  
+	  if (ok) {
+#if DEBUG
+	    tend = dtime_now();
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("*** Success by flipping TWO SEPARATED bits %d and %d of %d *** %.3f sec.\n", i, j, len, tend-tstart);
+#endif
+	    return;
+	  }
+	}
+#if DEBUGx
+	tend = dtime_now();
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf ("*** No luck flipping TWO SEPARATED bits of %d *** %.3f sec.\n", len, tend-tstart);
+#endif
+
+	return;
+}
+
+
+
+
+static int try_decode (rrbb_t block, int chan, int subchan, int alevel, retry_t bits_flipped, int flip_a, int flip_b, int flip_c)
+{
+	struct hdlc_state_s H;	
+	int blen;			/* Block length in bits. */
+	int i;
+	int raw;			/* From demodulator. */
+	int dbit;			/* Data bit after undoing NRZI. */
+
+
+	H.prev_raw = rrbb_get_bit (block, 0);	  /* Actually last bit of the */
+					/* opening flag so we can derive the */
+					/* first data bit.  */
+
+	/* Does this make sense? */
+	/* This is the last bit of the "flag" pattern. */
+	/* If it was corrupted we wouldn't have detected */
+	/* the start of frame. */
+
+	if (0 == flip_a || 0 == flip_b || 0 == flip_c){
+	  H.prev_raw = ! H.prev_raw;
+	}
+
+	H.pat_det = 0;
+	H.oacc = 0;
+	H.olen = 0;
+	H.frame_len = 0;
+
+	blen = rrbb_get_len (block);
+
+#if DEBUGx
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("try_decode: blen=%d\n", blen);
+#endif
+
+	for (i=1; i<blen; i++) {
+
+	  raw = rrbb_get_bit (block, i);
+
+	  if (i == flip_a || i == flip_b || i == flip_c){
+	    raw = ! raw;
+	  }
+
+/*
+ * Using NRZI encoding,
+ *   A '0' bit is represented by an inversion since previous bit.
+ *   A '1' bit is represented by no change.
+ */
+
+	  dbit = (raw == H.prev_raw);
+	  H.prev_raw = raw;
+
+/*
+ * Octets are sent LSB first.
+ * Shift the most recent 8 bits thru the pattern detector.
+ */
+	  H.pat_det >>= 1;
+	  if (dbit) {
+	    H.pat_det |= 0x80;
+	  }
+
+	  if (H.pat_det == 0x7e) {
+	    /* The special pattern 01111110 indicates beginning and ending of a frame. */
+#if DEBUGx
+	    text_color_set(DW_COLOR_DEBUG);
+	    dw_printf ("try_decode: found flag, i=%d\n", i);
+#endif
+	    return 0;
+	  }
+	  else if (H.pat_det == 0xfe) {
+	    /* Valid data will never have 7 one bits in a row. */
+#if DEBUGx
+	    text_color_set(DW_COLOR_DEBUG);
+	    dw_printf ("try_decode: found abort, i=%d\n", i);
+#endif
+ 	    return 0;
+	  }
+	  else if ( (H.pat_det & 0xfc) == 0x7c ) {
+/*
+ * If we have five '1' bits in a row, followed by a '0' bit,
+ *
+ *	0111110xx
+ *
+ * the current '0' bit should be discarded because it was added for 
+ * "bit stuffing."
+ */
+	    ;
+	  } else {
+
+/*
+ * In all other cases, accumulate bits into octets, and complete octets
+ * into the frame buffer.
+ */
+
+	    H.oacc >>= 1;
+	    if (dbit) {
+	      H.oacc |= 0x80;
+	    }
+	    H.olen++;
+
+	    if (H.olen == 8) {
+	      H.olen = 0;
+
+	      if (H.frame_len < MAX_FRAME_LEN) {
+		H.frame_buf[H.frame_len] = H.oacc;
+		H.frame_len++;
+	      }
+	    }
+	  }
+
+	}	/* end of loop on all bits in block */
+
+/* 
+ * Do we have a minimum number of complete bytes?
+ */
+
+#if DEBUGx
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("try_decode: olen=%d, frame_len=%d\n", H.olen, H.frame_len);
+#endif
+
+	if (H.olen == 0 && H.frame_len >= MIN_FRAME_LEN) {
+
+	  unsigned short actual_fcs, expected_fcs;
+
+#if DEBUGx 
+	  int j;
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("NEW WAY: frame len = %d\n", H.frame_len);
+	  for (j=0; j<H.frame_len; j++) {
+	    dw_printf ("  %02x", H.frame_buf[j]);
+	  }
+	  dw_printf ("\n");
+#endif
+	  /* Check FCS, low byte first, and process... */
+
+	  /* Alternatively, it is possible to include the two FCS bytes */
+	  /* in the CRC calculation and look for a magic constant.  */
+	  /* That would be easier in the case where the CRC is being */
+	  /* accumulated along the way as the octets are received. */
+	  /* I think making a second pass over it and comparing is */
+	  /* easier to understand. */
+
+	  actual_fcs = H.frame_buf[H.frame_len-2] | (H.frame_buf[H.frame_len-1] << 8);
+
+	  expected_fcs = fcs_calc (H.frame_buf, H.frame_len - 2);
+
+	  if (actual_fcs == expected_fcs && sanity_check (H.frame_buf, H.frame_len - 2, bits_flipped)) {
+
+	
+	      // TODO: Shouldn't be necessary to pass chan, subchan, alevel into
+	      // try_decode because we can obtain them from block.
+	      // Let's make sure that assumption is good...
+
+	      assert (rrbb_get_chan(block) == chan);
+	      assert (rrbb_get_subchan(block) == subchan);
+	      assert (rrbb_get_audio_level(block) == alevel);
+
+	      multi_modem_process_rec_frame (chan, subchan, H.frame_buf, H.frame_len - 2, alevel, bits_flipped);   /* len-2 to remove FCS. */
+	      return 1;		/* success */
+	  }
+	}
+	return 0;	/* failure. */
+
+} /* end try_decode */
+
+
+/*
+ * Try to weed out bogus packets from initially failed FCS matches.
+ */
+
+static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped)
+{
+	int alen;		/* Length of address part. */
+	int j;
+
+/*
+ * No sanity check if we didn't try fixing the data.
+ * Should we have different levels of checking depending on 
+ * how much we try changing the raw data?
+ */
+	if (bits_flipped == RETRY_NONE) {
+	  return 1;
+	}
+
+#if DEBUGx
+	text_color_set(DW_COLOR_XMIT);
+	dw_printf ("sanity_check: address part length = %d\n", alen);
+#endif
+
+/*
+ * Address part must be a multiple of 7. 
+ */
+
+	alen = 0;
+	for (j=0; j<blen && alen==0; j++) {
+	  if (buf[j] & 0x01) {
+	    alen = j + 1;
+	  }
+	}
+
+	if (alen % 7 != 0) {
+#if DEBUGx
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("sanity_check: FAILED.  Address part not multiple of 7.\n");
+#endif
+	  return 0;
+	}
+
+/*
+ * Need at least 2 addresses and maximum of 8 digipeaters. 
+ */
+
+	if (alen/7 < 2 || alen/7 > 10) {
+#if DEBUGx
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("sanity_check: FAILED.  Too few or many addresses.\n");
+#endif
+	  return 0;
+	}
+
+/* 
+ * Addresses can contain only upper case letters, digits, and space. 
+ */
+
+	for (j=0; j<alen; j+=7) {
+
+	  char addr[7];
+
+	  addr[0] = buf[j+0] >> 1;
+	  addr[1] = buf[j+1] >> 1;
+	  addr[2] = buf[j+2] >> 1;
+	  addr[3] = buf[j+3] >> 1;
+	  addr[4] = buf[j+4] >> 1;
+	  addr[5] = buf[j+5] >> 1;
+	  addr[6] = '\0';
+
+
+	  if ( (! isupper(addr[0]) && ! isdigit(addr[0])) ||
+	       (! isupper(addr[1]) && ! isdigit(addr[1]) && addr[1] != ' ') ||
+	       (! isupper(addr[2]) && ! isdigit(addr[2]) && addr[2] != ' ') ||
+	       (! isupper(addr[3]) && ! isdigit(addr[3]) && addr[3] != ' ') ||
+	       (! isupper(addr[4]) && ! isdigit(addr[4]) && addr[4] != ' ') ||
+	       (! isupper(addr[5]) && ! isdigit(addr[5]) && addr[5] != ' ')) {
+#if DEBUGx	  
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("sanity_check: FAILED.  Invalid characters in addresses \"%s\"\n", addr);
+#endif
+	    return 0;
+	  }
+	}
+
+/*
+ * The next two bytes should be 0x03 and 0xf0 for APRS.
+ * Checking that would mean precluding use for other types of packet operation.
+ *
+ * The next section is also assumes APRS and might discard good data
+ * for other protocols.   
+ */
+
+
+/*
+ * Finally, look for bogus characters in the information part.
+ * In theory, the bytes could have any values.
+ * In practice, we find only printable ASCII characters and:
+ *	
+ *	0x0a	line feed
+ *	0x0d	carriage return	
+ *	0x1c	MIC-E
+ *	0x1d	MIC-E
+ *	0x1e	MIC-E
+ *	0x1f	MIC-E
+ *	0x7f	MIC-E
+ *	0x80	"{UIV32N}<0x0d><0x9f><0x80>"
+ *	0x9f	"{UIV32N}<0x0d><0x9f><0x80>"
+ *	0xb0	degree symbol, ISO LATIN1
+ *		  (Note: UTF-8 uses two byte sequence 0xc2 0xb0.)
+ *	0xbe	invalid MIC-E encoding.
+ *	0xf8	degree symbol, Microsoft code page 437
+ *
+ * So, if we have something other than these (in English speaking countries!), 
+ * chances are that we have bogus data from twiddling the wrong bits.
+ *
+ * Notice that we shouldn't get here for good packets.  This extra level
+ * of checking happens only if we twiddled a couple of bits, possibly
+ * creating bad data.  We want to be very fussy.
+ */
+
+ 	for (j=alen+2; j<blen; j++) {
+	  int ch = buf[j];
+
+	  if ( ! (( ch >= 0x1c && ch <= 0x7f) 
+			|| ch == 0x0a 
+			|| ch == 0x0d
+			|| ch == 0x80
+			|| ch == 0x9f
+			|| ch == 0xb0
+			|| ch == 0xf8) ) {
+#if DEBUGx
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("sanity_check: FAILED.  Probably bogus info char 0x%02x\n", ch);
+#endif
+	    return 0;
+	  }
+	}
+
+	return 1;
+}
+
+
+/* end hdlc_rec2.c */
+
+
+
+
+// TODO:  Also in xmit.c.  Move to some common location.
+
+
+/* Current time in seconds but more resolution than time(). */
+
+/* We don't care what date a 0 value represents because we */
+/* only use this to calculate elapsed time. */
+
+
+#if DEBUG
+
+static double dtime_now (void)
+{
+#if __WIN32__
+	/* 64 bit integer is number of 100 nanosecond intervals from Jan 1, 1601. */
+
+	FILETIME ft;
+	
+	GetSystemTimeAsFileTime (&ft);
+
+	return ((( (double)ft.dwHighDateTime * (256. * 256. * 256. * 256.) + 
+			(double)ft.dwLowDateTime ) / 10000000.) - 11644473600.);
+#else
+	/* tv_sec is seconds from Jan 1, 1970. */
+
+	struct timespec ts;
+
+	clock_gettime (CLOCK_REALTIME, &ts);
+
+	return (ts.tv_sec + ts.tv_nsec / 1000000000.);
+#endif
+}
+
+#endif 
diff --git a/hdlc_rec2.h b/hdlc_rec2.h
new file mode 100755
index 0000000..9cef6ca
--- /dev/null
+++ b/hdlc_rec2.h
@@ -0,0 +1,35 @@
+
+#ifndef HDLC_REC2_H
+#define HDLC_REC2_H 1
+
+
+#include "rrbb.h"
+#include "ax25_pad.h"	/* for packet_t */
+
+typedef enum retry_e {
+		RETRY_NONE=0,
+		RETRY_SINGLE=1,
+		RETRY_DOUBLE=2,
+		RETRY_TRIPLE=3,
+		RETRY_TWO_SEP=4 } retry_t;
+
+#if defined(DIREWOLF_C) || defined(ATEST_C) || defined(UDPTEST_C)
+
+static const char * retry_text[] = {
+		"NONE",
+		"SINGLE",
+		"DOUBLE",
+		"TRIPLE",
+		"TWO_SEP" };
+#endif
+
+void hdlc_rec2_block (rrbb_t block, retry_t fix_bits);
+
+void hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int alevel);
+
+/* Provided by the top level application to process a complete frame. */
+
+void app_process_rec_packet (int chan, int subchan, packet_t pp, int level, retry_t retries, char *spectrum);
+
+
+#endif
diff --git a/hdlc_send.c b/hdlc_send.c
new file mode 100755
index 0000000..1c4ffe8
--- /dev/null
+++ b/hdlc_send.c
@@ -0,0 +1,215 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+#include <stdio.h>
+
+#include "direwolf.h"
+#include "hdlc_send.h"
+#include "audio.h"
+#include "gen_tone.h"
+#include "textcolor.h"
+#include "fcs_calc.h"
+
+static void send_control (int, int);
+static void send_data (int, int);
+static void send_bit (int, int);
+
+static int number_of_bits_sent;
+
+
+
+/*-------------------------------------------------------------
+ *
+ * Name:	hdlc_send
+ *
+ * Purpose:	Convert HDLC frames to a stream of bits.
+ *
+ * Inputs:	chan	- Audio channel number, 0 = first.
+ *
+ *		fbuf	- Frame buffer address.
+ *
+ *		flen	- Frame length, not including the FCS.
+ *
+ * Outputs:	Bits are shipped out by calling tone_gen_put_bit().
+ *
+ * Returns:	Number of bits sent including "flags" and the
+ *		stuffing bits.  
+ *		The required time can be calculated by dividing this
+ *		number by the transmit rate of bits/sec.
+ *
+ * Description:	Convert to stream of bits including:
+ *			start flag
+ *			bit stuffed data
+ *			calculated FCS
+ *			end flag
+ *		NRZI encoding
+ *
+ * 
+ * Assumptions:	It is assumed that the tone_gen module has been
+ *		properly initialized so that bits sent with 
+ *		tone_gen_put_bit() are processed correctly.
+ *
+ *--------------------------------------------------------------*/
+
+
+int hdlc_send_frame (int chan, unsigned char *fbuf, int flen)
+{
+	int j, fcs;
+	
+
+	number_of_bits_sent = 0;
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("hdlc_send_frame ( chan = %d, fbuf = %p, flen = %d )\n", chan, fbuf, flen);
+	fflush (stdout);
+#endif
+
+
+	send_control (chan, 0x7e);	/* Start frame */
+	
+	for (j=0; j<flen; j++) {
+	  send_data (chan, fbuf[j]);
+	}
+
+	fcs = fcs_calc (fbuf, flen);
+
+	send_data (chan, fcs & 0xff);
+	send_data (chan, (fcs >> 8) & 0xff);
+
+	send_control (chan, 0x7e);	/* End frame */
+
+	return (number_of_bits_sent);
+}
+
+
+/*-------------------------------------------------------------
+ *
+ * Name:	hdlc_send_flags
+ *
+ * Purpose:	Send HDLC flags before and after the frame.
+ *
+ * Inputs:	chan	- Audio channel number, 0 = first.
+ *
+ *		nflags	- Number of flag patterns to send.
+ *
+ *		finish	- True for end of transmission.
+ *			  This causes the last audio buffer to be flushed.
+ *
+ * Outputs:	Bits are shipped out by calling tone_gen_put_bit().
+ *
+ * Returns:	Number of bits sent.  
+ *		There is no bit-stuffing so we would expect this to
+ *		be 8 * nflags.
+ *		The required time can be calculated by dividing this
+ *		number by the transmit rate of bits/sec.
+ *
+ * Assumptions:	It is assumed that the tone_gen module has been
+ *		properly initialized so that bits sent with 
+ *		tone_gen_put_bit() are processed correctly.
+ *
+ *--------------------------------------------------------------*/
+
+int hdlc_send_flags (int chan, int nflags, int finish)
+{
+	int j;
+	
+
+	number_of_bits_sent = 0;
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("hdlc_send_flags ( chan = %d, nflags = %d, finish = %d )\n", chan, nflags, finish);
+	fflush (stdout);
+#endif
+
+	/* The AX.25 spec states that when the transmitter is on but not sending data */
+	/* it should send a continuous stream of "flags." */
+
+	for (j=0; j<nflags; j++) {
+	  send_control (chan, 0x7e);
+	}
+
+/* Push out the final partial buffer! */
+
+	if (finish) {
+	  audio_flush();
+	}
+
+	return (number_of_bits_sent);
+}
+
+
+
+static int stuff = 0;
+
+static void send_control (int chan, int x) 
+{
+	int i;
+
+	for (i=0; i<8; i++) {
+	  send_bit (chan, x & 1);
+	  x >>= 1;
+	}
+	
+	stuff = 0;
+}
+
+static void send_data (int chan, int x) 
+{
+	int i;
+
+	for (i=0; i<8; i++) {
+	  send_bit (chan, x & 1);
+	  if (x & 1) {
+	    stuff++;
+	    if (stuff == 5) {
+	      send_bit (chan, 0);
+	      stuff = 0;
+	    }
+	  } else {
+	    stuff = 0;
+          }
+	  x >>= 1;
+	}
+}
+
+/*
+ * NRZI encoding.
+ * data 1 bit -> no change.
+ * data 0 bit -> invert signal.
+ */
+
+static void send_bit (int chan, int b)
+{
+	static int output;
+
+	if (b == 0) {
+	  output = ! output;
+	}
+
+	tone_gen_put_bit (chan, output);
+
+	number_of_bits_sent++;
+}
+
+/* end hdlc_send.c */
\ No newline at end of file
diff --git a/hdlc_send.h b/hdlc_send.h
new file mode 100755
index 0000000..dc6c304
--- /dev/null
+++ b/hdlc_send.h
@@ -0,0 +1,10 @@
+
+/* hdlc_send.h */
+
+int hdlc_send_frame (int chan, unsigned char *fbuf, int flen);
+
+int hdlc_send_flags (int chan, int flags, int finish);
+
+/* end hdlc_send.h */
+
+
diff --git a/igate.c b/igate.c
new file mode 100755
index 0000000..5f4601a
--- /dev/null
+++ b/igate.c
@@ -0,0 +1,1491 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013, 2014  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      igate.c
+ *
+ * Purpose:   	IGate client.
+ *		
+ * Description:	Establish connection with a tier 2 IGate server
+ *		and relay packets between RF and Internet.
+ *
+ * References:	APRS-IS (Automatic Packet Reporting System-Internet Service)
+ *		http://www.aprs-is.net/Default.aspx
+ *
+ *		APRS iGate properties
+ *		http://wiki.ham.fi/APRS_iGate_properties
+ *
+ *---------------------------------------------------------------*/
+
+/*------------------------------------------------------------------
+ *
+ * From http://windows.microsoft.com/en-us/windows7/ipv6-frequently-asked-questions
+ * 
+ * How can I enable IPv6?
+ * Follow these steps:
+ * 
+ * Open Network Connections by clicking the Start button, and then clicking 
+ * Control Panel. In the search box, type adapter, and then, under Network 
+ * and Sharing Center, click View network connections.
+ * 
+ * Right-click your network connection, and then click Properties.   
+ * If you're prompted for an administrator password or confirmation, type 
+ * the password or provide confirmation.
+ *
+ * Select the check box next to Internet Protocol Version 6 (TCP/IPv6).
+ *
+ *---------------------------------------------------------------*/
+
+/*
+ * Native Windows:	Use the Winsock interface.
+ * Linux:		Use the BSD socket interface.
+ * Cygwin:		Can use either one.
+ */
+
+
+#if __WIN32__
+
+/* The goal is to support Windows XP and later. */
+
+#include <winsock2.h>
+// default is 0x0400
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501	/* Minimum OS version is XP. */
+//#define _WIN32_WINNT 0x0502	/* Minimum OS version is XP with SP2. */
+//#define _WIN32_WINNT 0x0600	/* Minimum OS version is Vista. */
+#include <ws2tcpip.h>
+#else 
+#include <stdlib.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <string.h>
+
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "version.h"
+#include "digipeater.h"
+#include "tq.h"
+#include "igate.h"
+#include "latlong.h"
+
+
+
+#if __WIN32__
+static unsigned __stdcall connnect_thread (void *arg);
+static unsigned __stdcall igate_recv_thread (void *arg);
+#else
+static void * connnect_thread (void *arg);
+static void * igate_recv_thread (void *arg);
+#endif
+
+static void send_msg_to_server (char *msg);
+static void xmit_packet (char *message);
+
+static void rx_to_ig_init (void);
+static void rx_to_ig_remember (packet_t pp);
+static int rx_to_ig_allow (packet_t pp);
+
+static void ig_to_tx_init (void);
+static void ig_to_tx_remember (packet_t pp);
+static int ig_to_tx_allow (packet_t pp);
+
+
+/* 
+ * File descriptor for socket to IGate server. 
+ * Set to -1 if not connected. 
+ * (Don't use SOCKET type because it is unsigned.) 
+*/
+
+static volatile int igate_sock = -1;	
+
+/*
+ * After connecting to server, we want to make sure
+ * that the login sequence is sent first.
+ * This is set to true after the login is complete.
+ */
+
+static volatile int ok_to_send = 0;
+
+
+
+
+/*
+ * Convert Internet address to text.
+ * Can't use InetNtop because it is supported only on Windows Vista and later. 
+ */
+
+static char * ia_to_text (int  Family, void * pAddr, char * pStringBuf, size_t StringBufSize)
+{
+	struct sockaddr_in *sa4;
+	struct sockaddr_in6 *sa6;
+
+	switch (Family) {
+	  case AF_INET:
+	    sa4 = (struct sockaddr_in *)pAddr;
+#if __WIN32__
+	    sprintf (pStringBuf, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1,
+						sa4->sin_addr.S_un.S_un_b.s_b2,
+						sa4->sin_addr.S_un.S_un_b.s_b3,
+						sa4->sin_addr.S_un.S_un_b.s_b4);
+#else
+	    inet_ntop (AF_INET, &(sa4->sin_addr), pStringBuf, StringBufSize);
+#endif
+	    break;
+	  case AF_INET6:
+	    sa6 = (struct sockaddr_in6 *)pAddr;
+#if __WIN32__
+	    sprintf (pStringBuf, "%x:%x:%x:%x:%x:%x:%x:%x",  
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[0]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[3]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[4]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[5]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[6]),
+					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[7]));
+#else
+	    inet_ntop (AF_INET6, &(sa6->sin6_addr), pStringBuf, StringBufSize);
+#endif
+	    break;
+	  default:
+	    sprintf (pStringBuf, "Invalid address family!");
+	}
+	assert (strlen(pStringBuf) < StringBufSize);
+	return pStringBuf;
+}
+
+
+#if ITEST
+
+/* For unit testing. */
+
+int main (int argc, char *argv[])
+{
+	struct igate_config_s igate_config;
+	struct digi_config_s digi_config;
+	packet_t pp;
+
+	memset (&igate_config, 0, sizeof(igate_config));
+
+	strcpy (igate_config.t2_server_name, "localhost");
+	igate_config.t2_server_port = 14580;
+	strcpy (igate_config.t2_login, "WB2OSZ-JL");
+	strcpy (igate_config.t2_passcode, "-1");
+	igate_config.t2_filter = strdup ("r/1/2/3");
+	
+	igate_config.tx_chan = 0;
+	strcpy (igate_config.tx_via, ",WIDE2-1");
+	igate_config.tx_limit_1 = 3;
+	igate_config.tx_limit_5 = 5;
+
+	memset (&digi_config, 0, sizeof(digi_config));
+	digi_config.num_chans = 2;
+	strcpy (digi_config.mycall[0], "WB2OSZ-1");
+	strcpy (digi_config.mycall[1], "WB2OSZ-2");
+
+	igate_init(&igate_config, &digi_config);
+
+	while (igate_sock == -1) {
+	  SLEEP_SEC(1);
+	}
+
+	SLEEP_SEC (2);
+	pp = ax25_from_text ("A>B,C,D:Ztest message 1", 0);
+	igate_send_rec_packet (0, pp);
+	ax25_delete (pp);
+
+	SLEEP_SEC (2);
+	pp = ax25_from_text ("A>B,C,D:Ztest message 2", 0);
+	igate_send_rec_packet (0, pp);
+	ax25_delete (pp);
+
+	SLEEP_SEC (2);
+	pp = ax25_from_text ("A>B,C,D:Ztest message 2", 0);   /* Should suppress duplicate. */
+	igate_send_rec_packet (0, pp);
+	ax25_delete (pp);
+
+	SLEEP_SEC (2);
+	pp = ax25_from_text ("A>B,TCPIP,D:ZShould drop this due to path", 0);
+	igate_send_rec_packet (0, pp);
+	ax25_delete (pp);
+
+	SLEEP_SEC (2);
+	pp = ax25_from_text ("A>B,C,D:?Should drop query", 0);
+	igate_send_rec_packet (0, pp);
+	ax25_delete (pp);
+
+	SLEEP_SEC (5);
+	pp = ax25_from_text ("A>B,C,D:}E>F,G*,H:Zthird party stuff", 0);
+	igate_send_rec_packet (0, pp);
+	ax25_delete (pp);
+
+#if 1
+	while (1) {
+	  SLEEP_SEC (20);
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf ("Send received packet\n");
+	  send_msg_to_server ("W1ABC>APRS:?\r\n");
+	}
+#endif
+	return 0;
+}
+
+#endif
+
+
+/*
+ * Global stuff (to this file)
+ *
+ * These are set by init function and need to 
+ * be kept around in case connection is lost and
+ * we need to reestablish the connection later.
+ */
+
+static struct igate_config_s g_config;
+
+static int g_num_chans;			/* Number of radio channels. */
+
+static char g_mycall[MAX_CHANS][AX25_MAX_ADDR_LEN];
+					/* Call-ssid associated */
+                                        /* with each of the radio channels.  */
+                                        /* Could be the same or different. */
+
+
+/*
+ * Statistics.  
+ * TODO: need print function.
+ */
+
+static int stats_failed_connect;	/* Number of times we tried to connect to */
+					/* a server and failed.  A small number is not */
+					/* a bad thing.  Each name should have a bunch */
+					/* of addresses for load balancing and */
+					/* redundancy. */
+
+static int stats_connects;		/* Number of successful connects to a server. */
+					/* Normally you'd expect this to be 1.  */
+					/* Could be larger if one disappears and we */
+					/* try again to find a different one. */
+
+static time_t stats_connect_at;		/* Most recent time connection was established. */
+					/* can be used to determine elapsed connect time. */
+
+static int stats_rf_recv_packets;	/* Number of candidate packets from the radio. */
+
+static int stats_rx_igate_packets;	/* Number of packets passed along to the IGate */
+					/* server after filtering. */
+
+static int stats_uplink_bytes;		/* Total number of bytes sent to IGate server */
+					/* including login, packets, and hearbeats. */
+
+static int stats_downlink_bytes;	/* Total number of bytes from IGate server including */
+					/* packets, heartbeats, other messages. */
+
+static int stats_tx_igate_packets;	/* Number of packets from IGate server. */
+
+static int stats_rf_xmit_packets;	/* Number of packets passed along to radio */
+					/* after rate limiting or other restrictions. */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        igate_init
+ *
+ * Purpose:     One time initialization when main application starts up.
+ *
+ * Inputs:	p_igate_config	- IGate configuration.
+ *
+ *		p_digi_config	- Digipeater configuration.  All we care about is:
+ *				  - Number of radio channels.
+ *				  - Radio call and SSID for each channel.
+ *
+ * Description:	This starts two threads:
+ *
+ *		  *  to establish and maintain a connection to the server.
+ *		  *  to listen for packets from the server.
+ *
+ *--------------------------------------------------------------------*/
+
+
+void igate_init (struct igate_config_s *p_igate_config, struct digi_config_s *p_digi_config)
+{
+#if __WIN32__
+	HANDLE connnect_th;
+	HANDLE cmd_recv_th;
+#else
+	pthread_t connect_listen_tid;
+	pthread_t cmd_listen_tid;
+	int e;
+#endif
+	int j;
+
+#if DEBUGx
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("igate_init ( %s, %d, %s, %s, %s )\n", 
+				p_igate_config->t2_server_name, 
+				p_igate_config->t2_server_port, 
+				p_igate_config->t2_login, 
+				p_igate_config->t2_passcode, 
+				p_igate_config->t2_filter);
+#endif
+
+
+	stats_failed_connect = 0;	
+	stats_connects = 0;		
+	stats_connect_at = 0;		
+	stats_rf_recv_packets = 0;	
+	stats_rx_igate_packets = 0;	
+	stats_uplink_bytes = 0;		
+	stats_downlink_bytes = 0;	
+	stats_tx_igate_packets = 0;	
+	stats_rf_xmit_packets = 0;
+	
+	rx_to_ig_init ();
+	ig_to_tx_init ();
+/*
+ * Save the arguments for later use.
+ */
+	memcpy (&g_config, p_igate_config, sizeof (g_config));
+
+	g_num_chans = p_digi_config->num_chans;
+	assert (g_num_chans >= 1 && g_num_chans <= MAX_CHANS);
+	for (j=0; j<g_num_chans; j++) {
+	  strcpy (g_mycall[j], p_digi_config->mycall[j]);
+	}
+
+
+/*
+ * Continue only if we have server name, login, and passcode.
+ */
+	if (strlen(p_igate_config->t2_server_name) == 0 ||
+	    strlen(p_igate_config->t2_login) == 0 ||
+	    strlen(p_igate_config->t2_passcode) == 0) {
+	  return;
+	}
+
+/*
+ * This connects to the server and sets igate_sock.
+ * It also sends periodic messages to say I'm still here.
+ */
+
+#if __WIN32__
+	connnect_th = (HANDLE)_beginthreadex (NULL, 0, connnect_thread, (void *)NULL, 0, NULL);
+	if (connnect_th == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Internal error: Could not create IGate connection thread\n");
+	  return;
+	}
+#else
+	e = pthread_create (&connect_listen_tid, NULL, connnect_thread, (void *)NULL);
+	if (e != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror("Internal error: Could not create IGate connection thread");
+	  return;
+	}
+#endif
+
+/*
+ * This reads messages from client when igate_sock is valid.
+ */
+
+#if __WIN32__
+	cmd_recv_th = (HANDLE)_beginthreadex (NULL, 0, igate_recv_thread, NULL, 0, NULL);
+	if (cmd_recv_th == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Internal error: Could not create IGate reading thread\n");
+	  return;
+	}
+#else
+	e = pthread_create (&cmd_listen_tid, NULL, igate_recv_thread, NULL);
+	if (e != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror("Internal error: Could not create IGate reading thread");
+	  return;
+	}
+#endif
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        connnect_thread
+ *
+ * Purpose:     Establish connection with IGate server.
+ *		Send periodic heartbeat to keep keep connection active.
+ *		Reconnect if something goes wrong and we got disconnected.
+ *
+ * Inputs:	arg		- Not used.
+ *
+ * Outputs:	igate_sock	- File descriptor for communicating with client app.
+ *				  Will be -1 if not connected.
+ *
+ * References:	TCP client example.
+ *		http://msdn.microsoft.com/en-us/library/windows/desktop/ms737591(v=vs.85).aspx
+ *
+ *		Linux IPv6 HOWTO
+ *		http://www.tldp.org/HOWTO/Linux+IPv6-HOWTO/
+ *
+ *--------------------------------------------------------------------*/
+
+/*
+ * Addresses don't get mixed up very well.
+ * IPv6 always shows up last so we'd probably never
+ * end up using any of them.  Use our own shuffle.
+ */
+
+static void shuffle (struct addrinfo *host[], int nhosts)
+{
+        int j, k;
+
+        assert (RAND_MAX >= nhosts);  /* for % to work right */
+
+        if (nhosts < 2) return;
+
+        srand (time(NULL));
+
+        for (j=0; j<nhosts; j++) {
+          k = rand() % nhosts;
+          assert (k >=0 && k<nhosts);
+          if (j != k) {
+            struct addrinfo *temp;
+            temp = host[j]; host[j] = host[k]; host[k] = temp;
+          }
+        }
+}
+
+#define MAX_HOSTS 50
+
+
+
+
+#if __WIN32__
+static unsigned __stdcall connnect_thread (void *arg)
+#else
+static void * connnect_thread (void *arg)	
+#endif	
+{
+	struct addrinfo hints;
+	struct addrinfo *ai_head = NULL;
+	struct addrinfo *ai;
+	struct addrinfo *hosts[MAX_HOSTS];
+	int num_hosts, n;
+	int err;
+	char server_port_str[12];	/* text form of port number */
+	char ipaddr_str[46];		/* text form of IP address */
+#if __WIN32__
+	WSADATA wsadata;
+#endif
+
+	sprintf (server_port_str, "%d", g_config.t2_server_port);
+#if DEBUGx
+	text_color_set(DW_COLOR_DEBUG);
+        dw_printf ("DEBUG: igate connect_thread start, port = %d = '%s'\n", g_config.t2_server_port, server_port_str);
+#endif
+
+#if __WIN32__
+	err = WSAStartup (MAKEWORD(2,2), &wsadata);
+	if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("WSAStartup failed: %d\n", err);
+	    return (0);
+	}
+
+	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
+	  text_color_set(DW_COLOR_ERROR);
+          dw_printf("Could not find a usable version of Winsock.dll\n");
+          WSACleanup();
+	  //sleep (1);
+          return (0);
+	}
+#endif
+
+	memset (&hints, 0, sizeof(hints));
+
+	hints.ai_family = AF_UNSPEC;	/* Allow either IPv4 or IPv6. */
+
+	// IPv6 is half baked on Windows XP.
+	// We might need to leave out IPv6 support for Windows version.
+	// hints.ai_family = AF_INET;	/* IPv4 only. */
+
+#if IPV6_ONLY
+	/* IPv6 addresses always show up at end of list. */
+	/* Force use of them for testing. */
+	hints.ai_family = AF_INET6;	/* IPv6 only */
+#endif					
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_protocol = IPPROTO_TCP;
+
+
+/*
+ * Repeat forever.
+ */
+
+	while (1) {
+
+/*
+ * Connect to IGate server if not currently connected.
+ */
+
+	  if (igate_sock == -1) {
+
+	    SLEEP_SEC (5);
+
+	    ai_head = NULL;
+	    err = getaddrinfo(g_config.t2_server_name, server_port_str, &hints, &ai_head);
+	    if (err != 0) {
+	      text_color_set(DW_COLOR_ERROR);
+#if __WIN32__
+	      dw_printf ("Can't get address for IGate server %s, err=%d\n", 
+					g_config.t2_server_name, WSAGetLastError());
+#else 
+	      dw_printf ("Can't get address for IGate server %s, %s\n", 
+					g_config.t2_server_name, gai_strerror(err));
+#endif
+	      freeaddrinfo(ai_head);
+
+	      continue;
+	    }
+
+#if DEBUG_DNS
+	    text_color_set(DW_COLOR_DEBUG);
+	    dw_printf ("getaddrinfo returns:\n");
+#endif
+	    num_hosts = 0;
+	    for (ai = ai_head; ai != NULL; ai = ai->ai_next) {
+#if DEBUG_DNS
+	      text_color_set(DW_COLOR_DEBUG);
+	      ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, sizeof(ipaddr_str));
+	      dw_printf ("    %s\n", ipaddr_str);
+#endif
+	      hosts[num_hosts] = ai;
+	      if (num_hosts < MAX_HOSTS) num_hosts++;
+	    }
+
+	    // We can get multiple addresses back for the host name.
+	    // These should be somewhat randomized for load balancing. 
+	    // It turns out the IPv6 addresses are always at the 
+	    // end for both Windows and Linux.   We do our own shuffling
+	    // to mix them up better and give IPv6 a chance. 
+
+	    shuffle (hosts, num_hosts);
+
+#if DEBUG_DNS
+	    text_color_set(DW_COLOR_DEBUG);
+	    dw_printf ("after shuffling:\n");
+	    for (n=0; n<num_hosts; n++) {
+	      ia_to_text (hosts[n]->ai_family, hosts[n]->ai_addr, ipaddr_str, sizeof(ipaddr_str));
+	      dw_printf ("    %s\n", ipaddr_str);
+	    }
+#endif
+
+	    // Try each address until we find one that is successful.
+
+	    for (n=0; n<num_hosts; n++) {
+	      int is;
+
+	      ai = hosts[n];
+
+	      ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, sizeof(ipaddr_str));
+	      is = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+#if __WIN32__
+	      if (is == INVALID_SOCKET) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("IGate: Socket creation failed, err=%d", WSAGetLastError());
+	        WSACleanup();
+	        is = -1;
+		stats_failed_connect++;
+	        continue;
+	      }
+#else
+	      if (err != 0) {
+	        text_color_set(DW_COLOR_INFO);
+	        dw_printf("Connect to IGate server %s (%s) failed.\n\n",
+					g_config.t2_server_name, ipaddr_str);
+	        (void) close (is);
+	        is = -1;
+		stats_failed_connect++;
+	        continue;
+	      }
+#endif
+
+#ifndef DEBUG_DNS 
+	      err = connect(is, ai->ai_addr, (int)ai->ai_addrlen);
+#if __WIN32__
+	      if (err == SOCKET_ERROR) {
+	        text_color_set(DW_COLOR_INFO);
+	        dw_printf("Connect to IGate server %s (%s) failed.\n\n",
+					g_config.t2_server_name, ipaddr_str);
+	        closesocket (is);
+	        is = -1;
+		stats_failed_connect++; 
+	        continue;
+	      }
+	      // TODO: set TCP_NODELAY?
+#else
+	      if (err != 0) {
+	        text_color_set(DW_COLOR_INFO);
+	        dw_printf("Connect to IGate server %s (%s) failed.\n\n",
+					g_config.t2_server_name, ipaddr_str);
+	        (void) close (is);
+	        is = -1;
+		stats_failed_connect++;
+	        continue;
+	      }
+	      /* IGate documentation says to use it.  */
+	      /* Does it really make a difference for this application? */
+	      int flag = 1;
+	      err = setsockopt (is, IPPROTO_TCP, TCP_NODELAY, (void*)(long)(&flag), sizeof(flag));
+	      if (err < 0) {
+	        text_color_set(DW_COLOR_INFO);
+	        dw_printf("setsockopt TCP_NODELAY failed.\n");
+	      }
+#endif
+	      stats_connects++;
+	      stats_connect_at = time(NULL);
+
+/* Success. */
+
+	      text_color_set(DW_COLOR_INFO);
+ 	      dw_printf("\nNow connected to IGate server %s (%s)\n", g_config.t2_server_name, ipaddr_str );
+	      if (strchr(ipaddr_str, ':') != NULL) {
+	      	dw_printf("Check server status here http://[%s]:14501\n\n", ipaddr_str);
+	      }
+	      else {
+	        dw_printf("Check server status here http://%s:14501\n\n", ipaddr_str);
+	      }
+
+/* 
+ * Set igate_sock so everyone else can start using it. 
+ * But make the Rx -> Internet messages wait until after login.
+ */
+
+	      ok_to_send = 0;
+	      igate_sock = is;
+#endif	  
+	      break;
+	    }
+
+	    freeaddrinfo(ai_head);
+
+	    if (igate_sock != -1) {
+	      char stemp[256];
+
+/* 
+ * Send login message.
+ * Software name and version must not contain spaces.
+ */
+
+	      SLEEP_SEC(3);
+	      sprintf (stemp, "user %s pass %s vers Dire-Wolf %d.%d", 
+			g_config.t2_login, g_config.t2_passcode,
+			MAJOR_VERSION, MINOR_VERSION);
+	      if (g_config.t2_filter != NULL) {
+	        strcat (stemp, " filter ");
+	        strcat (stemp, g_config.t2_filter);
+	      }
+	      strcat (stemp, "\r\n");
+	      send_msg_to_server (stemp);
+
+/* Delay until it is ok to start sending packets. */
+
+	      SLEEP_SEC(7);
+	      ok_to_send = 1;
+	    }
+	  }
+
+/*
+ * If connected to IGate server, send heartbeat periodically to keep connection active.
+ */
+	  if (igate_sock != -1) {
+	    SLEEP_SEC(10);
+	  }
+	  if (igate_sock != -1) {
+	    SLEEP_SEC(10);
+	  }
+	  if (igate_sock != -1) {
+	    SLEEP_SEC(10);
+	  }
+
+
+	  if (igate_sock != -1) {
+
+	    char heartbeat[10];
+
+	    strcpy (heartbeat, "#\r\n");
+
+	    /* This will close the socket if any error. */
+	    send_msg_to_server (heartbeat);
+
+	  }
+	}
+} /* end connnect_thread */
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        igate_send_rec_packet
+ *
+ * Purpose:     Send a packet to the IGate server
+ *
+ * Inputs:	chan	- Radio channel it was received on.
+ *
+ *		recv_pp	- Pointer to packet object.
+ *			  *** CALLER IS RESPONSIBLE FOR DELETING IT! **
+ *		
+ *
+ * Description:	Send message to IGate Server if connected.
+ *
+ * Assumptions:	(1) Caller has already verified it is an APRS packet.
+ *		i.e. control = 3 for UI frame, protocol id = 0xf0 for no layer 3
+ *
+ *		(2) This is being called only for packets received with
+ *		a correct CRC.  We don't want to propagate corrupted data.
+ *
+ *--------------------------------------------------------------------*/
+
+void igate_send_rec_packet (int chan, packet_t recv_pp)
+{
+	packet_t pp;
+	int n;
+	unsigned char *pinfo;
+	char *p;
+	char msg[520];		/* Message to IGate max 512 characters. */
+	int info_len;
+	
+
+	if (igate_sock == -1) {
+	  return;	/* Silently discard if not connected. */
+	}
+
+	if ( ! ok_to_send) {
+	  return;	/* Login not complete. */
+	}
+
+	/* Count only while connected. */
+	stats_rf_recv_packets++;
+
+/*
+ * First make a copy of it because it might be modified in place.
+ */
+
+	pp = ax25_dup (recv_pp);
+
+/*
+ * Third party frames require special handling to unwrap payload.
+ */
+	while (ax25_get_dti(pp) == '}') {
+	  packet_t inner_pp;
+
+	  for (n = 0; n < ax25_get_num_repeaters(pp); n++) {
+	    char via[AX25_MAX_ADDR_LEN];	/* includes ssid. Do we want to ignore it? */
+
+	    ax25_get_addr_with_ssid (pp, n + AX25_REPEATER_1, via);
+
+	    if (strcmp(via, "TCPIP") == 0 ||
+	        strcmp(via, "TCPXX") == 0 ||
+	        strcmp(via, "RFONLY") == 0 ||
+	        strcmp(via, "NOGATE") == 0) {
+#if DEBUGx
+	      text_color_set(DW_COLOR_DEBUG);
+	      dw_printf ("Rx IGate: Do not relay with TCPIP etc. in path.\n");
+#endif
+	      ax25_delete (pp);
+	      return;
+	    }
+	  }
+
+#if DEBUGx
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("Rx IGate: Unwrap third party message.\n");
+#endif
+	  inner_pp = ax25_unwrap_third_party(pp);
+	  if (inner_pp == NULL) {
+	    ax25_delete (pp);
+	    return;
+	  }
+	  ax25_delete (pp);
+	  pp = inner_pp;
+	}
+
+/* 
+ * Do not relay packets with TCPIP, TCPXX, RFONLY, or NOGATE in the via path.
+ */
+	for (n = 0; n < ax25_get_num_repeaters(pp); n++) {
+	  char via[AX25_MAX_ADDR_LEN];	/* includes ssid. Do we want to ignore it? */
+
+	  ax25_get_addr_with_ssid (pp, n + AX25_REPEATER_1, via);
+
+	  if (strcmp(via, "TCPIP") == 0 ||
+	      strcmp(via, "TCPXX") == 0 ||
+	      strcmp(via, "RFONLY") == 0 ||
+	      strcmp(via, "NOGATE") == 0) {
+#if DEBUGx
+	    text_color_set(DW_COLOR_DEBUG);
+	    dw_printf ("Rx IGate: Do not relay with TCPIP etc. in path.\n");
+#endif
+	    ax25_delete (pp);
+	    return;
+	  }
+	}
+
+/*
+ * Do not relay generic query.
+ */
+	if (ax25_get_dti(pp) == '?') {
+#if DEBUGx
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("Rx IGate: Do not relay generic query.\n");
+#endif
+	  ax25_delete (pp);
+	  return;
+	}
+
+
+/*
+ * Cut the information part at the first CR or LF.
+ */
+
+	info_len = ax25_get_info (pp, &pinfo);
+
+	if ((p = strchr ((char*)pinfo, '\r')) != NULL) {
+#if DEBUGx
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("Rx IGate: Truncated information part at CR.\n");
+#endif
+          *p = '\0';
+	}
+
+	if ((p = strchr ((char*)pinfo, '\n')) != NULL) {
+#if DEBUGx
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("Rx IGate: Truncated information part at LF.\n");
+#endif
+          *p = '\0';
+	}
+
+
+/*
+ * Someone around here occasionally sends a packet with no information part.
+ */
+	if (strlen(pinfo) == 0) {
+
+#if DEBUGx
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("Rx IGate: Information part length is zero.\n");
+#endif
+	  ax25_delete (pp);
+	  return;
+	}
+
+// TODO: Should we drop raw touch tone data object type generated here?
+
+/*
+ * Do not relay if a duplicate of something sent recently.
+ */
+
+	if ( ! rx_to_ig_allow(pp)) {
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("Rx IGate: Drop duplicate of same packet seen recently.\n");
+#endif
+	  ax25_delete (pp);
+	  return;
+	}
+
+/* 
+ * Finally, append ",qAR," and my call to the path.
+ */
+
+	ax25_format_addrs (pp, msg);
+	msg[strlen(msg)-1] = '\0';    /* Remove trailing ":" */
+	strcat (msg, ",qAR,");
+	strcat (msg, g_mycall[chan]);
+	strcat (msg, ":");
+	strcat (msg, (char*)pinfo);
+	strcat (msg, "\r\n");
+
+	send_msg_to_server (msg);
+	stats_rx_igate_packets++;
+
+/*
+ * Remember what was sent to avoid duplicates in near future.
+ */
+	rx_to_ig_remember (pp);
+
+	ax25_delete (pp);
+
+} /* end igate_send_rec_packet */
+
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        send_msg_to_server
+ *
+ * Purpose:     Send to the IGate server.
+ *		This one function should be used for login, hearbeats,
+ *		and packets.
+ *
+ * Inputs:	msg	- Message.  Should end with CR/LF.
+ *		
+ *
+ * Description:	Send message to IGate Server if connected.
+ *		Disconnect from server, and notify user, if any error.
+ *
+ *--------------------------------------------------------------------*/
+
+
+static void send_msg_to_server (char *msg)
+{
+	int err;
+
+
+	if (igate_sock == -1) {
+	  return;	/* Silently discard if not connected. */
+	}
+
+	stats_uplink_bytes += strlen(msg);
+
+#if DEBUG
+	text_color_set(DW_COLOR_XMIT);
+	dw_printf ("[ig] ");
+	ax25_safe_print (msg, strlen(msg), 0);
+	dw_printf ("\n");
+#endif
+
+#if __WIN32__	
+        err = send (igate_sock, msg, strlen(msg), 0);
+	if (err == SOCKET_ERROR)
+	{
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("\nError %d sending message to IGate server.  Closing connection.\n\n", WSAGetLastError());
+	  //dw_printf ("DEBUG: igate_sock=%d, line=%d\n", igate_sock, __LINE__);
+	  closesocket (igate_sock);
+	  igate_sock = -1;
+	  WSACleanup();
+	}
+#else
+        err = write (igate_sock, msg, strlen(msg));
+	if (err <= 0)
+	{
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("\nError sending message to IGate server.  Closing connection.\n\n");
+	  close (igate_sock);
+	  igate_sock = -1;    
+	}
+#endif
+	
+} /* end send_msg_to_server */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        get1ch
+ *
+ * Purpose:     Read one byte from socket.
+ *
+ * Inputs:	igate_sock	- file handle for socket.
+ *
+ * Returns:	One byte from stream.
+ *		Waits and tries again later if any error.
+ *
+ *
+ *--------------------------------------------------------------------*/
+
+static int get1ch (void)
+{
+	unsigned char ch;
+	int n;
+
+	while (1) {
+
+	  while (igate_sock == -1) {
+	    SLEEP_SEC(5);			/* Not connected.  Try again later. */
+	  }
+
+	  /* Just get one byte at a time. */
+	  // TODO: might read complete packets and unpack from own buffer
+	  // rather than using a system call for each byte.
+
+#if __WIN32__
+	  n = recv (igate_sock, (char*)(&ch), 1, 0);
+#else
+	  n = read (igate_sock, &ch, 1);
+#endif
+
+	  if (n == 1) {
+#if DEBUG9
+	    dw_printf (log_fp, "%02x %c %c", ch, 
+			isprint(ch) ? ch : '.' , 
+			(isupper(ch>>1) || isdigit(ch>>1) || (ch>>1) == ' ') ? (ch>>1) : '.');
+	    if (ch == '\r') fprintf (log_fp, "  CR");
+	    if (ch == '\n') fprintf (log_fp, "  LF");
+	    fprintf (log_fp, "\n");
+#endif
+	    return(ch);	
+	  }
+
+          text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("\nError reading from IGate server.  Closing connection.\n\n");
+#if __WIN32__
+	  closesocket (igate_sock);
+#else
+	  close (igate_sock);
+#endif
+	  igate_sock = -1;
+	}
+
+} /* end get1ch */
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        igate_recv_thread
+ *
+ * Purpose:     Wait for messages from IGate Server.
+ *
+ * Inputs:	arg		- Not used.
+ *
+ * Outputs:	igate_sock	- File descriptor for communicating with client app.
+ *
+ * Description:	Process messages from the IGate server.
+ *
+ *--------------------------------------------------------------------*/
+
+#if __WIN32__
+static unsigned __stdcall igate_recv_thread (void *arg)
+#else
+static void * igate_recv_thread (void *arg)
+#endif
+{
+	unsigned char ch;
+	unsigned char message[1000];  // Spec says max 500 or so.
+	int len;
+	
+			
+#if DEBUGx
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("igate_recv_thread ( socket = %d )\n", igate_sock);
+#endif
+
+	while (1) {
+
+	  len = 0;
+
+	  do
+	  {
+	    ch = get1ch();
+	    stats_downlink_bytes++;
+
+	    if (len < sizeof(message)) 
+	    {
+	      message[len] = ch;
+	    }
+	    len++;
+	    
+	  } while (ch != '\n');
+
+/*
+ * We have a complete message terminated by LF.
+ */
+	  if (len == 0) 
+	  {
+/* 
+ * Discard if zero length. 
+ */
+	  }
+	  else if (message[0] == '#') {
+/*
+ * Heartbeat or other control message.
+ *
+ * Print only if within seconds of logging in.
+ * That way we can see login confirmation but not 
+ * be bothered by the heart beat messages.
+ */
+#ifndef DEBUG
+	    if ( ! ok_to_send) {
+#endif
+	      text_color_set(DW_COLOR_REC);
+	      dw_printf ("[ig] ");
+	      ax25_safe_print ((char *)message, len, 0);
+	      dw_printf ("\n");
+#ifndef DEBUG
+	    }
+#endif
+	  }
+	  else 
+	  {
+/*
+ * Convert to third party packet and transmit.
+ */
+	    text_color_set(DW_COLOR_REC);
+	    dw_printf ("\n[ig] ");
+	    ax25_safe_print ((char *)message, len, 0);
+	    dw_printf ("\n");
+
+/*
+ * Remove CR LF from end.
+ */
+	    if (len >=2 && message[len-1] == '\n') { message[len-1] = '\0'; len--; }
+	    if (len >=1 && message[len-1] == '\r') { message[len-1] = '\0'; len--; }
+
+	    xmit_packet ((char*)message);
+	  }
+
+	}  /* while (1) */
+	return (0);
+
+} /* end igate_recv_thread */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        xmit_packet
+ *
+ * Purpose:     Convert text string, from IGate server, to third party
+ *		packet and send to transmit queue.
+ *
+ * Inputs:	message		- As sent by the server.  
+ *
+ *--------------------------------------------------------------------*/
+
+static void xmit_packet (char *message)
+{
+	packet_t pp3;
+	char payload[500];	/* what is max len? */
+	char *pinfo = NULL;
+	int info_len;
+
+/*
+ * Is IGate to Radio direction enabled?
+ */
+	if (g_config.tx_chan == -1) {
+	  return;
+	}
+
+	stats_tx_igate_packets++;
+
+	assert (g_config.tx_chan >= 0 && g_config.tx_chan < MAX_CHANS);
+
+/*
+ * Try to parse it into a packet object.
+ * Bug:  Up to 8 digipeaters are allowed in radio format.
+ * There is a potential of finding more here.
+ */
+	pp3 = ax25_from_text(message, 0);
+	if (pp3 == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Tx IGate: Could not parse message from server.\n");
+	  dw_printf ("%s\n", message);
+	  return;
+	}
+
+/*
+ * TODO: Discard if qAX in path???  others?
+ */
+
+/*
+ * Remove the VIA path.
+ */
+	while (ax25_get_num_repeaters(pp3) > 0) {
+	  ax25_remove_addr (pp3, AX25_REPEATER_1);
+	}
+
+/* 
+ * Replace the VIA path with TCPIP and my call.
+ * Mark my call as having been used.
+ */
+	ax25_set_addr (pp3, AX25_REPEATER_1, "TCPIP");
+	ax25_set_h (pp3, AX25_REPEATER_1);
+	ax25_set_addr (pp3, AX25_REPEATER_2, g_mycall[g_config.tx_chan]); 
+	ax25_set_h (pp3, AX25_REPEATER_2);
+
+/*
+ * Convert to text representation.
+ */
+	ax25_format_addrs (pp3, payload);
+	info_len = ax25_get_info (pp3, (unsigned char **)(&pinfo));
+	strcat (payload, pinfo);
+#if DEBUGx
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("Tx IGate: payload=%s\n", payload);
+#endif
+	
+/*
+ * Encapsulate for sending over radio if no reason to drop it.
+ */
+	if (ig_to_tx_allow (pp3)) {
+	  char radio [500];
+	  packet_t pradio;
+
+	  sprintf (radio, "%s>%s%d%d%s:}%s",
+				g_mycall[g_config.tx_chan],
+				APP_TOCALL, MAJOR_VERSION, MINOR_VERSION,
+				g_config.tx_via,
+				payload);
+
+	  pradio = ax25_from_text (radio, 1);
+#if ITEST
+	  text_color_set(DW_COLOR_XMIT);
+	  dw_printf ("Xmit: %s\n", radio);
+	  ax25_delete (pradio);
+#else
+	  /* This consumes packet so don't reference it again! */
+	  tq_append (g_config.tx_chan, TQ_PRIO_1_LO, pradio);
+#endif
+	  stats_rf_xmit_packets++;
+	  ig_to_tx_remember (pp3);
+	}
+
+	ax25_delete (pp3);
+
+} /* end xmit_packet */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        rx_to_ig_remember
+ *
+ * Purpose:     Keep a record of packets sent to the IGate server
+ *		so we don't send duplicates within some set amount of time.
+ *
+ * Inputs:	pp	- Pointer to a packet object.
+ *
+ *-------------------------------------------------------------------
+ *
+ * Name:	rx_to_ig_allow
+ * 
+ * Purpose:	Check whether this is a duplicate of another sent recently.
+ *
+ * Input:	pp	- Pointer to packet object.
+ *		
+ * Returns:	True if it is OK to send.
+ *		
+ *-------------------------------------------------------------------
+ *
+ * Description: These two functions perform the final stage of filtering
+ *		before sending a received (from radio) packet to the IGate server.
+ *
+ *		rx_to_ig_remember must be called for every packet sent to the server.
+ *
+ *		rx_to_ig_allow decides whether this should be allowed thru
+ *		based on recent activity.  We will drop the packet if it is a
+ *		duplicate of another sent recently.
+ *
+ *		Rather than storing the entire packet, we just keep a CRC to 
+ *		reduce memory and processing requirements.  We do the same in
+ *		the digipeater function to suppress duplicates.
+ *
+ *		There is a 1 / 65536 chance of getting a false positive match
+ *		which is good enough for this application.
+ *
+ *--------------------------------------------------------------------*/
+
+#define RX2IG_DEDUPE_TIME 60		/* Do not send duplicate within 60 seconds. */
+#define RX2IG_HISTORY_MAX 30		/* Remember the last 30 sent to IGate server. */
+
+static int rx2ig_insert_next;
+static time_t rx2ig_time_stamp[RX2IG_HISTORY_MAX];
+static unsigned short rx2ig_checksum[RX2IG_HISTORY_MAX];
+
+static void rx_to_ig_init (void)
+{
+	int n;
+	for (n=0; n<RX2IG_HISTORY_MAX; n++) {
+	  rx2ig_time_stamp[n] = 0;
+	  rx2ig_checksum[n] = 0;
+	}
+	rx2ig_insert_next = 0;
+}
+	
+
+static void rx_to_ig_remember (packet_t pp)
+{
+       	rx2ig_time_stamp[rx2ig_insert_next] = time(NULL);
+        rx2ig_checksum[rx2ig_insert_next] = ax25_dedupe_crc(pp);
+
+        rx2ig_insert_next++;
+        if (rx2ig_insert_next >= RX2IG_HISTORY_MAX) {
+          rx2ig_insert_next = 0;
+        }
+}
+
+static int rx_to_ig_allow (packet_t pp)
+{
+	unsigned short crc = ax25_dedupe_crc(pp);
+	time_t now = time(NULL);
+	int j;
+
+	for (j=0; j<RX2IG_HISTORY_MAX; j++) {
+	  if (rx2ig_time_stamp[j] >= now - RX2IG_DEDUPE_TIME && rx2ig_checksum[j] == crc) {
+	    return 0;
+	  }
+	}
+	return 1;
+
+} /* end rx_to_ig_allow */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        ig_to_tx_remember
+ *
+ * Purpose:     Keep a record of packets sent from IGate server to radio transmitter
+ *		so we don't send duplicates within some set amount of time.
+ *
+ * Inputs:	pp	- Pointer to a packet object.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * Name:	ig_to_tx_allow
+ * 
+ * Purpose:	Check whether this is a duplicate of another sent recently
+ *		or if we exceed the transmit rate limits.
+ *
+ * Input:	pp	- Pointer to packet object.
+ *		
+ * Returns:	True if it is OK to send.
+ *		
+ *------------------------------------------------------------------------------
+ *
+ * Description: These two functions perform the final stage of filtering
+ *		before sending a packet from the IGate server to the radio.
+ *
+ *		ig_to_tx_remember must be called for every packet, from the IGate 
+ *		server, sent to the radio transmitter.
+ *
+ *		ig_to_tx_allow decides whether this should be allowed thru
+ *		based on recent activity.  We will drop the packet if it is a
+ *		duplicate of another sent recently.
+ *
+ *		This is the essentially the same as the pair of functions
+ *		above with one addition restriction.  
+ *
+ *		The typical residential Internet connection is about 10,000
+ *		times faster than the radio links we are using.  It would
+ *		be easy to completely saturate the radio channel if we are
+ *		not careful.
+ *
+ *		Besides looking for duplicates, this will also tabulate the 
+ *		number of packets sent during the past minute and past 5
+ *		minutes and stop sending if a limit is reached.
+ *
+ * Future?	We might also want to avoid transmitting if the same packet
+ *		was heard on the radio recently.  If everything is kept in
+ *		the same table, we'd need to distinguish between those from
+ *		the IGate server and those heard on the radio.
+ *		Those heard on the radio would not count toward the
+ *		1 and 5 minute rate limiting.
+ *		Maybe even provide informative information such as -
+ *		Tx IGate: Same packet heard recently from W1ABC and W9XYZ.
+ *
+ *		Of course, the radio encapsulation would need to be removed
+ *		and only the 3rd party packet inside compared.
+ *
+ *--------------------------------------------------------------------*/
+
+#define IG2TX_DEDUPE_TIME 60		/* Do not send duplicate within 60 seconds. */
+#define IG2TX_HISTORY_MAX 50		/* Remember the last 50 sent from server to radio. */
+
+static int ig2tx_insert_next;
+static time_t ig2tx_time_stamp[IG2TX_HISTORY_MAX];
+static unsigned short ig2tx_checksum[IG2TX_HISTORY_MAX];
+
+static void ig_to_tx_init (void)
+{
+	int n;
+	for (n=0; n<IG2TX_HISTORY_MAX; n++) {
+	  ig2tx_time_stamp[n] = 0;
+	  ig2tx_checksum[n] = 0;
+	}
+	ig2tx_insert_next = 0;
+}
+	
+
+static void ig_to_tx_remember (packet_t pp)
+{
+       	ig2tx_time_stamp[ig2tx_insert_next] = time(NULL);
+        ig2tx_checksum[ig2tx_insert_next] = ax25_dedupe_crc(pp);
+
+        ig2tx_insert_next++;
+        if (ig2tx_insert_next >= IG2TX_HISTORY_MAX) {
+          ig2tx_insert_next = 0;
+        }
+}
+
+static int ig_to_tx_allow (packet_t pp)
+{
+	unsigned short crc = ax25_dedupe_crc(pp);
+	time_t now = time(NULL);
+	int j;
+	int count_1, count_5;
+
+	for (j=0; j<IG2TX_HISTORY_MAX; j++) {
+	  if (ig2tx_time_stamp[j] >= now - IG2TX_DEDUPE_TIME && ig2tx_checksum[j] == crc) {
+	    text_color_set(DW_COLOR_INFO);
+	    dw_printf ("Tx IGate: Drop duplicate packet transmitted recently.\n");
+	    return 0;
+	  }
+	}
+	count_1 = 0;
+	count_5 = 0;
+	for (j=0; j<IG2TX_HISTORY_MAX; j++) {
+	  if (ig2tx_time_stamp[j] >= now - 60) count_1++;
+	  if (ig2tx_time_stamp[j] >= now - 300) count_5++;
+	}
+
+	if (count_1 >= g_config.tx_limit_1) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Tx IGate: Already transmitted maximum of %d packets in 1 minute.\n", g_config.tx_limit_1);
+	  return 0;
+	}
+	if (count_5 >= g_config.tx_limit_5) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Tx IGate: Already transmitted maximum of %d packets in 5 minutes.\n", g_config.tx_limit_5);
+	  return 0;
+	}
+
+	return 1;
+
+} /* end ig_to_tx_allow */
+
+/* end igate.c */
diff --git a/igate.h b/igate.h
new file mode 100755
index 0000000..aa47507
--- /dev/null
+++ b/igate.h
@@ -0,0 +1,65 @@
+
+/*----------------------------------------------------------------------------
+ * 
+ * Name:	igate.h
+ *
+ * Purpose:	Interface to the Internet Gateway functions.
+ *
+ *-----------------------------------------------------------------------------*/
+
+
+#ifndef IGATE_H
+#define IGATE_H 1
+
+
+#include "ax25_pad.h"
+#include "digipeater.h"
+
+#define DEFAULT_IGATE_PORT 14580
+
+
+struct igate_config_s {
+
+/*
+ * For logging into the IGate server.
+ */
+	char t2_server_name[40];	/* Tier 2 IGate server name. */
+
+	int t2_server_port;		/* Typically 14580. */
+
+	char t2_login[AX25_MAX_ADDR_LEN];/* e.g. WA9XYZ-15 */
+					/* Note that the ssid could be any two alphanumeric */
+					/* characters not just 1 thru 15. */
+					/* Could be same or different than the radio call(s). */
+					/* Not sure what the consequences would be. */
+
+	char t2_passcode[8];		/* Max. 5 digits. Could be "-1". */
+
+	char *t2_filter;		/* Optional filter for IS -> RF direction. */
+
+/*
+ * For transmitting.
+ */
+	int tx_chan;			/* Radio channel for transmitting. */
+					/* 0=first, etc.  -1 for none. */
+
+	char tx_via[80];		/* VIA path for transmitting third party packets. */
+					/* Usual text representation.  */
+					/* Must start with "," if not empty so it can */
+					/* simply be inserted after the destination address. */
+
+	int tx_limit_1;			/* Max. packets to transmit in 1 minute. */
+
+	int tx_limit_5;			/* Max. packets to transmit in 5 minutes. */
+};
+
+/* Call this once at startup */
+
+void igate_init (struct igate_config_s *p_igate_config, struct digi_config_s *p_digi_config);
+
+/* Call this with each packet received from the radio. */
+
+void igate_send_rec_packet (int chan, packet_t recv_pp);
+
+
+#endif
diff --git a/kiss.c b/kiss.c
new file mode 100755
index 0000000..b3a10b4
--- /dev/null
+++ b/kiss.c
@@ -0,0 +1,923 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      kiss.c
+ *
+ * Purpose:   	Act as a virtual KISS TNC for use by other packet radio applications.
+ *		
+ * Input:	
+ *
+ * Outputs:	  
+ *
+ * Description:	This provides a pseudo terminal for communication with a client application.
+ *
+ *		It implements the KISS TNC protocol as described in:
+ *		http://www.ka9q.net/papers/kiss.html
+ *
+ * 		Briefly, a frame is composed of 
+ *
+ *			* FEND (0xC0)
+ *			* Contents - with special escape sequences so a 0xc0
+ *				byte in the data is not taken as end of frame.
+ *				as part of the data.
+ *			* FEND
+ *
+ *		The first byte of the frame contains:
+ *	
+ *			* port number in upper nybble.
+ *			* command in lower nybble.
+ *
+ *	
+ *		Commands from application recognized:
+ *
+ *			0	Data Frame	AX.25 frame in raw format.
+ *
+ *			1	TXDELAY		See explanation in xmit.c.
+ *
+ *			2	Persistence	"	"
+ *
+ *			3 	SlotTime	"	"
+ *
+ *			4	TXtail		"	"
+ *						Spec says it is obsolete but Xastir
+ *						sends it and we respect it.
+ *
+ *			5	FullDuplex	Ignored.  Always full duplex.
+ *		
+ *			6	SetHardware	TNC specific.  Ignored.
+ *			
+ *			FF	Return		Exit KISS mode.  Ignored.
+ *
+ *
+ *		Messages sent to client application:
+ *
+ *			0	Data Frame	Received AX.25 frame in raw format.
+ *
+ *
+ *		
+ * Platform differences:
+ *
+ *		We can use a pseudo terminal for Linux or Cygwin applications.
+ *		However, Microsoft Windows doesn't seem to have similar functionality.
+ *		Native Windows applications expect to see a device named COM1,
+ *		COM2, COM3, or COM4.  Some might offer more flexibility but others
+ *		might be limited to these four choices.
+ *
+ *		The documentation instucts the user to install the com0com 
+ *		�Null-modem emulator� from http://sourceforge.net/projects/com0com/   
+ *		and configure it for COM3 & COM4.
+ *
+ *		By default Dire Wolf will use COM3 (/dev/ttyS2 or /dev/com3 - lower case!)
+ *		and the client application will use COM4 (available as /dev/ttyS or
+ *		/dev/com4 for Cygwin applications).
+ *
+ *
+ *		This can get confusing.
+ *
+ *		If __WIN32__ is defined, 
+ *			We use the Windows interface to the specfied serial port.
+ *			This could be a real serial port or the nullmodem driver
+ *			connected to another application.
+ *		
+ *		If __CYGWIN__ is defined,
+ *			We connect to a serial port as in the previous case but
+ *			use the Linux I/O interface.
+ *			We also supply a pseudo terminal for any Cygwin applications 
+ *			such as Xastir so the null modem is not needed.
+ *
+ *		For the Linux case,
+ *			We supply a pseudo terminal for use by other applications.
+ *
+ *
+ * Reference:	http://www.robbayer.com/files/serial-win.pdf
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+
+#if __WIN32__
+#include <stdlib.h>
+#include <windows.h>
+#else
+#define __USE_XOPEN2KXSI 1
+#define __USE_XOPEN 1
+//#define __USE_POSIX 1
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+#include "direwolf.h"
+#include "tq.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "kiss.h"
+#include "kiss_frame.h"
+#include "xmit.h"
+
+
+#if __WIN32__
+typedef HANDLE MYFDTYPE;
+#define MYFDERROR INVALID_HANDLE_VALUE
+#else
+typedef int MYFDTYPE;
+#define MYFDERROR (-1)
+#endif
+
+
+static kiss_frame_t kf;		/* Accumulated KISS frame and state of decoder. */
+
+
+/*
+ * These are for a Linux/Cygwin pseudo terminal.
+ */
+
+#if ! __WIN32__
+
+static MYFDTYPE pt_master_fd = MYFDERROR;	/* File descriptor for my end. */
+
+static MYFDTYPE pt_slave_fd = MYFDERROR;	/* File descriptor for pseudo terminal */
+						/* for use by application. */
+
+/*
+ * Symlink to pseudo terminal name which changes.
+ */
+
+#define DEV_KISS_TNC "/tmp/kisstnc"
+
+#endif
+
+/*
+ * This is for native Windows applications and a virtual null modem.
+ */
+
+#if __CYGWIN__ || __WIN32__
+
+static MYFDTYPE nullmodem_fd = MYFDERROR;
+
+#endif
+
+
+
+
+static void * kiss_listen_thread (void *arg);
+
+
+
+#if DEBUG9
+static FILE *log_fp;
+#endif
+
+
+static int kiss_debug = 0;		/* Print information flowing from and to client. */
+
+void kiss_serial_set_debug (int n) 
+{	
+	kiss_debug = n;
+}
+
+
+/* In server.c.  Should probably move to some misc. function file. */
+
+void hex_dump (unsigned char *p, int len);
+
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        kiss_init
+ *
+ * Purpose:     Set up a pseudo terminal acting as a virtual KISS TNC.
+ *		
+ *
+ * Inputs:	mc->nullmodem	- name of device for our end of nullmodem.
+ *
+ * Outputs:	
+ *
+ * Description:	(1) Create a pseudo terminal for the client to use.
+ *		(2) Start a new thread to listen for commands from client app
+ *		    so the main application doesn't block while we wait.
+ *
+ *
+ *--------------------------------------------------------------------*/
+
+static MYFDTYPE kiss_open_pt (void);
+static MYFDTYPE kiss_open_nullmodem (char *device);
+
+void kiss_init (struct misc_config_s *mc)
+{
+	int e;
+#if __WIN32__
+	HANDLE kiss_nullmodem_listen_th;
+#else
+	pthread_t kiss_pterm_listen_tid;
+	pthread_t kiss_nullmodem_listen_tid;
+#endif
+
+	memset (&kf, 0, sizeof(kf));
+
+/*
+ * This reads messages from client.
+ */
+
+#if ! __WIN32__
+
+/*
+ * Pseudo terminal for Cygwin and Linux versions.
+ */
+	pt_master_fd = MYFDERROR;
+
+	if (mc->enable_kiss_pt) {
+
+	  pt_master_fd = kiss_open_pt ();
+
+	  if (pt_master_fd != MYFDERROR) {
+	    e = pthread_create (&kiss_pterm_listen_tid, (pthread_attr_t*)NULL, kiss_listen_thread, (void*)(long)pt_master_fd);
+	    if (e != 0) {
+	      text_color_set(DW_COLOR_ERROR);
+	      perror("Could not create kiss listening thread for Linux pseudo terminal");
+	    }
+	  }
+	}
+	else {
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf ("Use -p command line option to enable KISS pseudo terminal.\n");
+	}
+#endif
+
+#if __CYGWIN__ || __WIN32
+
+/*
+ * Cygwin and native Windows versions have serial port connection.
+ */
+	if (strlen(mc->nullmodem) > 0) {
+
+#if ! __WIN32__
+
+	  /* Translate Windows device name into Linux name. */
+	  /* COM1 -> /dev/ttyS0, etc. */
+
+	  if (strncasecmp(mc->nullmodem, "COM", 3) == 0) {
+	    int n = atoi (mc->nullmodem + 3);
+	    text_color_set(DW_COLOR_INFO);
+	    dw_printf ("Converted nullmodem device '%s'", mc->nullmodem);
+	    if (n < 1) n = 1;
+	    sprintf (mc->nullmodem, "/dev/ttyS%d", n-1);
+	    dw_printf (" to Linux equivalent '%s'\n", mc->nullmodem);
+	  }
+#endif
+	  nullmodem_fd = kiss_open_nullmodem (mc->nullmodem);
+
+	  if (nullmodem_fd != MYFDERROR) {
+#if __WIN32__
+	    kiss_nullmodem_listen_th = _beginthreadex (NULL, 0, kiss_listen_thread, (void*)(long)nullmodem_fd, 0, NULL);
+	    if (kiss_nullmodem_listen_th == NULL) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Could not create kiss nullmodem thread\n");
+	      return;
+	    }
+#else
+	    e = pthread_create (&kiss_nullmodem_listen_tid, NULL, kiss_listen_thread, (void*)(long)nullmodem_fd);
+	    if (e != 0) {
+	      text_color_set(DW_COLOR_ERROR);
+	      perror("Could not create kiss listening thread for Windows virtual COM port.");
+	    
+	    }
+#endif
+	  }
+	}
+#endif
+
+
+#if DEBUG
+	text_color_set (DW_COLOR_DEBUG);
+#if ! __WIN32__
+	dw_printf ("end of kiss_init: pt_master_fd = %d\n", pt_master_fd);
+#endif
+#if __CYGWIN__ || __WIN32__
+	dw_printf ("end of kiss_init: nullmodem_fd = %d\n", nullmodem_fd);
+#endif
+
+#endif
+}
+
+
+/*
+ * Returns fd for master side of pseudo terminal or MYFDERROR for error.
+ */
+
+#if ! __WIN32__
+
+static MYFDTYPE kiss_open_pt (void)
+{
+	int fd;
+	char *slave_device;
+	struct termios ts;
+	int e;
+	//int flags;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("kiss_open_pt (  )\n");
+#endif
+	
+
+	fd = posix_openpt(O_RDWR|O_NOCTTY);
+
+	if (fd == MYFDERROR
+	    || grantpt (fd) == MYFDERROR
+	    || unlockpt (fd) == MYFDERROR
+	    || (slave_device = ptsname (fd)) == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("ERROR - Could not create pseudo terminal for KISS TNC.\n");
+	  return (MYFDERROR);
+	}
+
+
+	e = tcgetattr (fd, &ts);
+	if (e != 0) { 
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Can't get pseudo terminal attributes, err=%d\n", e);
+	  perror ("pt tcgetattr"); 
+	}
+
+	cfmakeraw (&ts);
+	
+	ts.c_cc[VMIN] = 1;	/* wait for at least one character */
+	ts.c_cc[VTIME] = 0;	/* no fancy timing. */
+				
+
+	e = tcsetattr (fd, TCSANOW, &ts);
+	if (e != 0) { 
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Can't set pseudo terminal attributes, err=%d\n", e);
+	  perror ("pt tcsetattr"); 
+	}
+
+/*
+ * After running for a while on Linux, the write eventually
+ * blocks if no one is reading from the other side of
+ * the pseudo terminal.  We get stuck on the kiss data
+ * write and reception stops.
+ *
+ * I tried using ioctl(,TIOCOUTQ,) to see how much was in 
+ * the queue but that always returned zero.  (Ubuntu)
+ *
+ * Let's try using non-blocking writes and see if we get
+ * the EWOULDBLOCK status instead of hanging.
+ */
+
+#if 0 	// this is worse. all writes fail. errno = 0 bad file descriptor
+	flags = fcntl(fd, F_GETFL, 0);
+	e = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
+	if (e != 0) { 
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Can't set pseudo terminal to nonblocking, fcntl returns %d, errno = %d\n", e, errno);
+	  perror ("pt fcntl"); 
+	}
+#endif
+#if 0  // same  
+	flags = 1;	
+	e = ioctl (fd, FIONBIO, &flags);
+	if (e != 0) { 
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Can't set pseudo terminal to nonblocking, ioctl returns %d, errno = %d\n", e, errno);
+	  perror ("pt ioctl"); 
+	}
+#endif
+	text_color_set(DW_COLOR_INFO);
+	dw_printf("Virtual KISS TNC is available on %s\n", slave_device);
+	dw_printf("WARNING - Dire Wolf will hang eventually if nothing is reading from it.\n");
+	
+/*
+ * The device name is not the same every time.
+ * This is inconvenient for the application because it might
+ * be necessary to change the device name in the configuration.
+ * Create a symlink, /tmp/kisstnc, so the application configuration
+ * does not need to change when the pseudo terminal name changes.
+ */
+
+//TODO: remove symlink on exit.
+	unlink (DEV_KISS_TNC);
+
+	if (symlink (slave_device, DEV_KISS_TNC) == 0) {
+	    dw_printf ("Created symlink %s -> %s\n", DEV_KISS_TNC, slave_device);
+	}
+	else {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Failed to create symlink %s\n", DEV_KISS_TNC);	
+	    perror ("");
+	}
+
+#if 1
+	// Sample code shows this. Why would we open it here?
+	// On Ubuntu, the slave side disappears after a few
+	// seconds if no one opens it.
+
+	pt_slave_fd = open(slave_device, O_RDWR|O_NOCTTY);
+
+	if (pt_slave_fd < 0)
+	   return MYFDERROR;
+#endif
+	return (fd);
+
+
+}
+
+#endif
+
+/*
+ * Returns fd for our side of null modem or MYFDERROR for error.
+ */
+
+
+#if __CYGWIN__ || __WIN32__
+
+static MYFDTYPE kiss_open_nullmodem (char *devicename)
+{
+
+#if __WIN32__
+
+	MYFDTYPE fd;
+	DCB dcb;
+	int ok;
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("kiss_open_nullmodem ( '%s' )\n", devicename);
+#endif
+	
+#if DEBUG9
+	log_fp = fopen ("kiss-debug.txt", "w");
+#endif
+
+// Need to use FILE_FLAG_OVERLAPPED for full duplex operation.
+// Without it, write blocks when waiting on read.
+
+// Read http://support.microsoft.com/kb/156932 
+
+
+	fd = CreateFile(devicename, GENERIC_READ | GENERIC_WRITE, 
+			0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+
+	if (fd == MYFDERROR) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("ERROR - Could not connect to %s side of null modem for Windows KISS TNC.\n", devicename);
+	  return (MYFDERROR);
+	}
+
+	/* Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363201(v=vs.85).aspx */
+
+	memset (&dcb, 0, sizeof(dcb));
+	dcb.DCBlength = sizeof(DCB);
+
+	ok = GetCommState (fd, &dcb);
+	if (! ok) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("kiss_open_nullmodem: GetCommState failed.\n");
+	}
+
+	/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */
+
+	// dcb.BaudRate ? shouldn't matter 
+	dcb.fBinary = 1;
+	dcb.fParity = 0;
+	dcb.fOutxCtsFlow = 0;
+	dcb.fOutxDsrFlow = 0;
+	dcb.fDtrControl = 0;
+	dcb.fDsrSensitivity = 0;
+	dcb.fOutX = 0;
+	dcb.fInX = 0;
+	dcb.fErrorChar = 0;
+	dcb.fNull = 0;		/* Don't drop nul characters! */
+	dcb.fRtsControl = 0;
+	dcb.ByteSize = 8;
+	dcb.Parity = NOPARITY;
+	dcb.StopBits = ONESTOPBIT;
+
+	ok = SetCommState (fd, &dcb);
+	if (! ok) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("kiss_open_nullmodem: SetCommState failed.\n");
+	}
+
+	text_color_set(DW_COLOR_INFO);
+	dw_printf("Virtual KISS TNC is connected to %s side of null modem.\n", devicename);
+
+#else
+
+/* Cygwin version. */
+
+	int fd;
+	struct termios ts;
+	int e;
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("kiss_open_nullmodem ( '%s' )\n", devicename);
+#endif
+
+	fd = open (devicename, O_RDWR);
+
+	if (fd == MYFDERROR) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("ERROR - Could not connect to %s side of null modem for Windows KISS TNC.\n", devicename);
+	  return (MYFDERROR);
+	}
+
+	e = tcgetattr (fd, &ts);
+	if (e != 0) { perror ("nm tcgetattr"); }
+
+	cfmakeraw (&ts);
+	
+	ts.c_cc[VMIN] = 1;	/* wait for at least one character */
+	ts.c_cc[VTIME] = 0;	/* no fancy timing. */
+
+	e = tcsetattr (fd, TCSANOW, &ts);
+	if (e != 0) { perror ("nm tcsetattr"); }
+
+	text_color_set(DW_COLOR_INFO);
+	dw_printf("Virtual KISS TNC is connected to %s side of null modem.\n", devicename);
+
+#endif
+
+	return (fd);
+}
+
+#endif
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        kiss_send_rec_packet
+ *
+ * Purpose:     Send a received packet or text string to the client app.
+ *
+ * Inputs:	chan		- Channel number where packet was received.
+ *				  0 = first, 1 = second if any.
+ *
+ *		pp		- Identifier for packet object.
+ *
+ *		fbuf		- Address of raw received frame buffer
+ *				  or a text string.
+ *
+ *		flen		- Length of raw received frame not including the FCS
+ *				  or -1 for a text string.
+ *		
+ *
+ * Description:	Send message to client.
+ *		We really don't care if anyone is listening or not.
+ *		I don't even know if we can find out.
+ *
+ *
+ *--------------------------------------------------------------------*/
+
+
+void kiss_send_rec_packet (int chan, unsigned char *fbuf,  int flen)
+{
+	unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN];
+	int kiss_len;
+	int j;
+	int err;
+
+#if ! __WIN32__
+	if (pt_master_fd == MYFDERROR) {
+	  return;
+	}
+#endif
+
+#if __CYGWIN__ || __WIN32__
+
+	if (nullmodem_fd == MYFDERROR) {
+	  return;
+	}
+#endif
+	
+	if (flen < 0) {
+	  flen = strlen((char*)fbuf);
+	  if (kiss_debug) {
+	    kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
+	  }
+	  strcpy ((char *)kiss_buff, (char *)fbuf);
+	  kiss_len = strlen((char *)kiss_buff);
+	}
+	else {
+
+	  kiss_len = 0;
+	  kiss_buff[kiss_len++] = FEND;
+	  kiss_buff[kiss_len++] = chan << 4;
+
+	  for (j=0; j<flen; j++) {
+
+	    if (fbuf[j] == FEND) {
+	      kiss_buff[kiss_len++] = FESC;
+	      kiss_buff[kiss_len++] = TFEND;
+	    }
+	    else if (fbuf[j] == FESC) {
+	      kiss_buff[kiss_len++] = FESC;
+	      kiss_buff[kiss_len++] = TFESC;
+	    }
+	    else {
+	      kiss_buff[kiss_len++] = fbuf[j];
+	    }
+	    assert (kiss_len < sizeof (kiss_buff));
+	  }
+	  kiss_buff[kiss_len++] = FEND;
+
+	  /* This has the escapes but not the surrounding FENDs. */
+
+	  if (kiss_debug) {
+	    kiss_debug_print (TO_CLIENT, NULL, kiss_buff+1, kiss_len-2);
+	  }
+
+	}
+
+#if ! __WIN32__
+
+/* Pseudo terminal for Cygwin and Linux. */
+
+
+        err = write (pt_master_fd, kiss_buff, (size_t)kiss_len);
+
+	if (err == -1 && errno == EWOULDBLOCK) {
+#if DEBUG 
+	  text_color_set (DW_COLOR_INFO);
+	  dw_printf ("KISS SEND - discarding message because write would block.\n");
+#endif
+	}
+	else if (err != kiss_len)
+	{
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("\nError sending KISS message to client application on pseudo terminal.  fd=%d, len=%d, write returned %d, errno = %d\n\n",
+		pt_master_fd, kiss_len, err, errno);
+	  perror ("pt write"); 
+	}
+
+#endif
+
+#if __CYGWIN__ || __WIN32__
+
+
+/*
+ * This write can block if nothing is connected to the other end.
+ * The solution is found in the com0com ReadMe file:
+ *
+ *	Q. My application hangs during its startup when it sends anything to one paired
+ *	   COM port. The only way to unhang it is to start HyperTerminal, which is connected
+ *	   to the other paired COM port. I didn't have this problem with physical serial
+ *	   ports.
+ *	A. Your application can hang because receive buffer overrun is disabled by
+ *	   default. You can fix the problem by enabling receive buffer overrun for the
+ *	   receiving port. Also, to prevent some flow control issues you need to enable
+ *	   baud rate emulation for the sending port. So, if your application use port CNCA0
+ *	   and other paired port is CNCB0, then:
+ *	
+ *	   1. Launch the Setup Command Prompt shortcut.
+ *	   2. Enter the change commands, for example:
+ *	
+ *	      command> change CNCB0 EmuOverrun=yes
+ *	      command> change CNCA0 EmuBR=yes
+ */
+
+#if __WIN32__
+
+	  DWORD nwritten; 
+
+	  /* Without this, write blocks while we are waiting on a read. */
+	  static OVERLAPPED ov_wr;
+	  memset (&ov_wr, 0, sizeof(ov_wr));
+
+          if ( ! WriteFile (nullmodem_fd, kiss_buff, kiss_len, &nwritten, &ov_wr))
+	  {
+	    err = GetLastError();
+	    if (err != ERROR_IO_PENDING) 
+	    {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("\nError sending KISS message to client application thru null modem.  Error %d.\n\n", (int)GetLastError());
+	      //CloseHandle (nullmodem_fd);
+	      //nullmodem_fd = MYFDERROR;
+	    }
+	  }
+	  else if (nwritten != flen) 
+	  {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("\nError sending KISS message to client application thru null modem.  Only %d of %d written.\n\n", (int)nwritten, kiss_len);
+	    //CloseHandle (nullmodem_fd);
+	    //nullmodem_fd = MYFDERROR;
+	  }
+
+#if DEBUG
+	  /* Could wait with GetOverlappedResult but we never */
+	  /* have an issues in this direction. */
+	  //text_color_set(DW_COLOR_DEBUG);
+	  //dw_printf ("KISS SEND completed.  wrote %d / %d\n", nwritten, kiss_len);
+#endif
+
+#else
+          err = write (nullmodem_fd, kiss_buf, (size_t)kiss_len);
+	  if (err != len)
+	  {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("\nError sending KISS message to client application thru null modem. err=%d\n\n", err);
+	    //close (nullmodem_fd);
+	    //nullmodem_fd = MYFDERROR;
+	  }
+#endif
+
+#endif
+
+} /* kiss_send_rec_packet */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        kiss_listen_thread
+ *
+ * Purpose:     Wait for messages from an application.
+ *
+ * Inputs:	arg		- File descriptor for reading.
+ *
+ * Outputs:	pt_slave_fd	- File descriptor for communicating with client app.
+ *
+ * Description:	Process messages from the client application.
+ *
+ *--------------------------------------------------------------------*/
+
+//TODO: should pass fd by reference so it can be zapped.
+//BUG: If we close it here, that fact doesn't get back 
+// to the main receiving thread.
+
+/* Return one byte (value 0 - 255) or terminate thread on error. */
+
+
+static int kiss_get (MYFDTYPE fd)
+{
+	unsigned char ch;
+
+#if __WIN32__		/* Native Windows version. */
+
+	DWORD n;	
+	static OVERLAPPED ov_rd;
+
+	memset (&ov_rd, 0, sizeof(ov_rd));
+	ov_rd.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
+
+	
+	/* Overlapped I/O makes reading rather complicated. */
+	/* See:  http://msdn.microsoft.com/en-us/library/ms810467.aspx */
+
+	/* It seems that the read completes OK with a count */
+	/* of 0 every time we send a message to the serial port. */
+
+	n = 0;	/* Number of characters read. */
+
+  	while (n == 0) {
+
+	  if ( ! ReadFile (fd, &ch, 1, &n, &ov_rd)) 
+	  {
+	    int err1 = GetLastError();
+
+	    if (err1 == ERROR_IO_PENDING) 
+	    {
+	      /* Wait for completion. */
+
+	      if (WaitForSingleObject (ov_rd.hEvent, INFINITE) == WAIT_OBJECT_0) 
+	      {
+	        if ( ! GetOverlappedResult (fd, &ov_rd, &n, 1))
+	        {
+	          int err3 = GetLastError();
+
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("\nKISS GetOverlappedResult error %d.\n\n", err3);
+	        }
+	        else 
+	        {
+		  /* Success!  n should be 1 */
+	        }
+	      }
+	    }
+	    else
+	    {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("\nKISS ReadFile error %d. Closing connection.\n\n", err1);
+	      //CloseHandle (fd);
+	      //fd = MYFDERROR;
+	      //pthread_exit (NULL);
+	    }
+	  }
+
+	}	/* end while n==0 */
+
+	CloseHandle(ov_rd.hEvent); 
+
+	if (n != 1) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("\nKISS failed to get one byte. n=%d.\n\n", (int)n);
+
+#if DEBUG9
+	  fprintf (log_fp, "n=%d\n", n);
+#endif
+	}
+
+
+#else		/* Linux/Cygwin version */
+
+	int n;
+
+	n = read(fd, &ch, (size_t)1);
+
+	if (n != 1) {
+	  //text_color_set(DW_COLOR_ERROR);
+	  //dw_printf ("\nError receiving kiss message from client application.  Closing connection %d.\n\n", fd);
+
+	  close (fd);
+
+	  fd = MYFDERROR;
+	  pthread_exit (NULL);
+	}
+
+#endif
+
+#if DEBUGx
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("kiss_get(%d) returns 0x%02x\n", fd, ch);
+#endif
+
+#if DEBUG9
+	fprintf (log_fp, "%02x %c %c", ch, 
+			isprint(ch) ? ch : '.' , 
+			(isupper(ch>>1) || isdigit(ch>>1) || (ch>>1) == ' ') ? (ch>>1) : '.');
+	if (ch == FEND) fprintf (log_fp, "  FEND");
+	if (ch == FESC) fprintf (log_fp, "  FESC");
+	if (ch == TFEND) fprintf (log_fp, "  TFEND");
+	if (ch == TFESC) fprintf (log_fp, "  TFESC");
+	if (ch == '\r') fprintf (log_fp, "  CR");
+	if (ch == '\n') fprintf (log_fp, "  LF");
+	fprintf (log_fp, "\n");
+	if (ch == FEND) fflush (log_fp);
+#endif
+	return (ch);
+}
+
+
+
+
+static void * kiss_listen_thread (void *arg)
+{
+	MYFDTYPE fd = (MYFDTYPE)(long)arg;
+
+	unsigned char ch;
+			
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("kiss_listen_thread ( %d )\n", fd);
+#endif
+
+
+	while (1) {
+	  ch = kiss_get(fd);
+
+	  if (kiss_frame (&kf, ch, kiss_debug, kiss_send_rec_packet)) { 
+	    kiss_process_msg (&kf, kiss_debug);
+	  }
+	}	/* while (1) */
+
+	return (NULL);	/* Unreachable but avoids compiler warning. */
+}
+
+/* end kiss.c */
diff --git a/kiss.h b/kiss.h
new file mode 100755
index 0000000..83563c4
--- /dev/null
+++ b/kiss.h
@@ -0,0 +1,21 @@
+
+/* 
+ * Name:	kiss.h
+ */
+
+
+#include "ax25_pad.h"		/* for packet_t */
+
+#include "config.h"
+
+
+
+
+void kiss_init (struct misc_config_s *misc_config);
+
+void kiss_send_rec_packet (int chan, unsigned char *fbuf,  int flen);
+
+void kiss_serial_set_debug (int n);
+
+
+/* end kiss.h */
diff --git a/kiss_frame.c b/kiss_frame.c
new file mode 100755
index 0000000..5d3d066
--- /dev/null
+++ b/kiss_frame.c
@@ -0,0 +1,407 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      kiss_frame.c
+ *
+ * Purpose:   	Common code used by Serial port and network versions of KISS protocol.
+ *		
+ * Description: The KISS TNS protocol is described in http://www.ka9q.net/papers/kiss.html
+ *
+ * 		Briefly, a frame is composed of 
+ *
+ *			* FEND (0xC0)
+ *			* Contents - with special escape sequences so a 0xc0
+ *				byte in the data is not taken as end of frame.
+ *				as part of the data.
+ *			* FEND
+ *
+ *		The first byte of the frame contains:
+ *	
+ *			* port number in upper nybble.
+ *			* command in lower nybble.
+ *
+ *	
+ *		Commands from application recognized:
+ *
+ *			0	Data Frame	AX.25 frame in raw format.
+ *
+ *			1	TXDELAY		See explanation in xmit.c.
+ *
+ *			2	Persistence	"	"
+ *
+ *			3 	SlotTime	"	"
+ *
+ *			4	TXtail		"	"
+ *						Spec says it is obsolete but Xastir
+ *						sends it and we respect it.
+ *
+ *			5	FullDuplex	Ignored.  Always full duplex.
+ *		
+ *			6	SetHardware	TNC specific.  Ignored.
+ *			
+ *			FF	Return		Exit KISS mode.  Ignored.
+ *
+ *
+ *		Messages sent to client application:
+ *
+ *			0	Data Frame	Received AX.25 frame in raw format.
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "kiss_frame.h"
+#include "tq.h"
+#include "xmit.h"
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        kiss_frame 
+ *
+ * Purpose:     Extract a KISS frame from byte stream.
+ *
+ * Inputs:	kf	- Current state of building a frame.
+ *		ch	- A byte from the input stream.
+ *		debug	- Activates debug output.
+ *		sendfun	- Function to send something to the client application.
+ *
+ * Outputs:	kf	- Current state is updated.
+ *
+ * Returns:	TRUE when a complete frame is ready for processing.
+ *
+ * Bug:		For send, the debug output shows exactly what is
+ *		being sent including the surrounding FEND and any
+ *		escapes.  For receive, we don't show those.
+ *
+ *-----------------------------------------------------------------*/
+
+/*
+ * Application might send some commands to put TNC into KISS mode.  
+ * For example, APRSIS32 sends something like:
+ *
+ *	<0x0d>
+ *	<0x0d>
+ *	XFLOW OFF<0x0d>
+ *	FULLDUP OFF<0x0d>
+ *	KISS ON<0x0d>
+ *	RESTART<0x0d>
+ *	<0x03><0x03><0x03>
+ *	TC 1<0x0d>
+ *	TN 2,0<0x0d><0x0d><0x0d>
+ *	XFLOW OFF<0x0d>
+ *	FULLDUP OFF<0x0d>
+ *	KISS ON<0x0d>
+ *	RESTART<0x0d>
+ *
+ * This keeps repeating over and over and over and over again if
+ * it doesn't get any sort of response.
+ *
+ * Let's try to keep it happy by sending back a command prompt.
+ */
+
+int kiss_frame (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int)) 
+{
+	
+	switch (kf->state) {
+	 
+  	  case KS_SEARCHING:		/* Searching for starting FEND. */
+
+	    if (ch == FEND) {
+	      
+	      /* Start of frame.  But first print any collected noise for debugging. */
+
+	      if (kf->noise_len > 0) {
+		if (debug) {
+		  kiss_debug_print (FROM_CLIENT, "Rejected Noise", kf->noise, kf->noise_len);
+	        }
+		kf->noise_len = 0;
+	      }
+	      
+	      kf->kiss_len = 0;
+	      kf->state = KS_COLLECTING;
+	      return 0;
+	    }
+
+	    /* Noise to be rejected. */
+
+	    if (kf->noise_len < MAX_NOISE_LEN) {
+	      kf->noise[kf->noise_len++] = ch;
+	    }
+	    if (ch == '\r') {
+	      if (debug) {
+		kiss_debug_print (FROM_CLIENT, "Rejected Noise", kf->noise, kf->noise_len);
+	   	kf->noise[kf->noise_len] = '\0';
+	      }
+
+	      /* Try to appease it by sending something back. */
+	      if (strcasecmp("restart\r", (char*)(kf->noise)) == 0 ||
+		    strcasecmp("reset\r", (char*)(kf->noise)) == 0) {
+	   	  (*sendfun) (0, (unsigned char *)"\xc0\xc0", -1);
+	      }
+	      else {
+	   	  (*sendfun) (0, (unsigned char *)"\r\ncmd:", -1);
+	      }
+	      kf->noise_len = 0;
+	    }
+	    return 0;
+
+	  case KS_COLLECTING:		/* Frame collection in progress. */
+
+	    if (ch == FEND) {
+	      
+	      /* End of frame. */
+
+	      if (kf->kiss_len == 0) {
+		/* Empty frame.  Just go on collecting. */
+	        return 0;
+	      }
+
+	      if (debug) {
+	        kiss_debug_print (FROM_CLIENT, NULL, kf->kiss_msg, kf->kiss_len);
+	      }
+	      kf->state = KS_SEARCHING;
+	      return 1;
+	    }
+
+	    if (kf->kiss_len < MAX_KISS_LEN) {
+	      kf->kiss_msg[kf->kiss_len++] = ch;
+	    }
+	    else {	    
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("KISS message exceeded maximum length.\n");
+	    }	      
+	    return 0;
+
+	  case KS_ESCAPE:		/* Expecting TFESC or TFEND. */
+
+	    if (kf->kiss_len >= MAX_KISS_LEN) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("KISS message exceeded maximum length.\n");
+	      kf->state = KS_COLLECTING;
+	      return 0;
+	    }	      
+
+	    if (ch == TFESC) {
+	      kf->kiss_msg[kf->kiss_len++] = FESC;
+	    }
+	    else if (ch == TFEND) {
+	      kf->kiss_msg[kf->kiss_len++] = FEND;
+	    }
+	    else {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("KISS protocol error.  TFESC or TFEND expected.\n");
+	    }
+	    
+	    kf->state = KS_COLLECTING;
+	    return 0;
+	}
+	
+	return 0;	/* unreachable but suppress compiler warning. */
+
+} /* end kiss_frame */   
+	      	    
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        kiss_process_msg 
+ *
+ * Purpose:     Process a message from the KISS client.
+ *
+ * Inputs:	kf	- Current state of building a frame.
+ *			  Should be complete.
+ *
+ *		debug	- Debug option is selected.
+ *
+ *-----------------------------------------------------------------*/
+
+void kiss_process_msg (kiss_frame_t *kf, int debug)
+{
+	int port;
+	int cmd;
+	packet_t pp;
+
+	port = (kf->kiss_msg[0] >> 4) & 0xf;
+	cmd = kf->kiss_msg[0] & 0xf;
+
+	switch (cmd) 
+	{
+	  case 0:				/* Data Frame */
+
+	    /* Special hack - Discard apparently bad data from Linux AX25. */
+
+	    if ((port == 2 || port == 8) && 
+		 kf->kiss_msg[1] == 'Q' << 1 &&
+		 kf->kiss_msg[2] == 'S' << 1 &&
+		 kf->kiss_msg[3] == 'T' << 1 &&
+		 kf->kiss_msg[4] == ' ' << 1 &&
+		 kf->kiss_msg[15] == 3 &&
+		 kf->kiss_msg[16] == 0xcd) {
+	        
+	      if (debug) {
+	         text_color_set(DW_COLOR_ERROR);
+	         dw_printf ("Special case - Drop packets which appear to be in error.\n");
+	      }
+	      return;
+	    }
+	
+	    pp = ax25_from_frame (kf->kiss_msg+1, kf->kiss_len-1, -1);
+	    if (pp == NULL) {
+	       text_color_set(DW_COLOR_ERROR);
+	       dw_printf ("ERROR - Invalid KISS data frame from client app.\n");
+	    }
+	    else {
+
+	    /* How can we determine if it is an original or repeated message? */
+	    /* If there is at least one digipeater in the frame, AND */
+	    /* that digipeater has been used, it should go out quickly thru */
+	    /* the high priority queue. */
+	    /* Otherwise, it is an original for the low priority queue. */
+
+	    if (ax25_get_num_repeaters(pp) >= 1 &&
+	      ax25_get_h(pp,AX25_REPEATER_1)) {
+	      tq_append (port, TQ_PRIO_0_HI, pp);
+	    }
+	    else {
+	      tq_append (port, TQ_PRIO_1_LO, pp);
+	    }
+	  }
+	  break;
+
+        case 1:				/* TXDELAY */
+
+          text_color_set(DW_COLOR_INFO);
+	  dw_printf ("KISS protocol set TXDELAY = %d, port %d\n", kf->kiss_msg[1], port);
+	  xmit_set_txdelay (port, kf->kiss_msg[1]);
+	  break;
+
+        case 2:				/* Persistence */
+
+          text_color_set(DW_COLOR_INFO);
+	  dw_printf ("KISS protocol set Persistence = %d, port %d\n", kf->kiss_msg[1], port);
+	  xmit_set_persist (port, kf->kiss_msg[1]);
+	  break;
+
+        case 3:				/* SlotTime */
+
+          text_color_set(DW_COLOR_INFO);
+	  dw_printf ("KISS protocol set SlotTime = %d, port %d\n", kf->kiss_msg[1], port);
+	  xmit_set_slottime (port, kf->kiss_msg[1]);
+	  break;
+
+        case 4:				/* TXtail */
+
+          text_color_set(DW_COLOR_INFO);
+	  dw_printf ("KISS protocol set TXtail = %d, port %d\n", kf->kiss_msg[1], port);
+	  xmit_set_txtail (port, kf->kiss_msg[1]);
+	  break;
+
+        case 5:				/* FullDuplex */
+
+          text_color_set(DW_COLOR_INFO);
+	  dw_printf ("KISS protocol set FullDuplex = %d, port %d\n", kf->kiss_msg[1], port);
+	  break;
+
+        case 6:				/* TNC specific */
+
+          text_color_set(DW_COLOR_INFO);
+	  dw_printf ("KISS protocol set hardware - ignored.\n");
+	  break;
+
+        case 15:				/* End KISS mode, port should be 15. */
+						/* Ignore it. */
+          text_color_set(DW_COLOR_INFO);
+	  dw_printf ("KISS protocol end KISS mode\n");
+	  break;
+
+        default:			
+          text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("KISS Invalid command %d\n", cmd);
+          kiss_debug_print (FROM_CLIENT, NULL, kf->kiss_msg, kf->kiss_len);
+	  break;
+	}
+
+} /* end kiss_process_msg */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        kiss_debug_print 
+ *
+ * Purpose:     Print message to/from client for debugging.
+ *
+ * Inputs:	fromto		- Direction of message.
+ *		special		- Comment if not a KISS frame.
+ *		pmsg		- Address of the message block.
+ *		msg_len		- Length of the message.
+ *
+ *--------------------------------------------------------------------*/
+
+
+/* In server.c.  Should probably move to some misc. function file. */
+
+void hex_dump (unsigned char *p, int len);
+
+
+
+void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len)
+{
+	const char *direction [2] = { "from", "to" };
+	const char *prefix [2] = { "<<<", ">>>" };
+	const char *function[16] = { 
+		"Data frame",	"TXDELAY",	"P",		"SlotTime",
+		"TXtail",	"FullDuplex",	"SetHardware",	"Invalid 7",
+		"Invalid 8", 	"Invalid 9",	"Invalid 10",	"Invalid 11",
+		"Invalid 12", 	"Invalid 13",	"Invalid 14",	"Return" };
+
+
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("\n");
+
+	if (special == NULL) {
+	  dw_printf ("%s %s %s KISS client application, port %d, total length = %d\n",
+			prefix[(int)fromto], function[pmsg[0] & 0xf], direction[(int)fromto], 
+			(pmsg[0] >> 4) & 0xf, msg_len);
+	}
+	else {
+	  dw_printf ("%s %s %s KISS client application, total length = %d\n",
+			prefix[(int)fromto], special, direction[(int)fromto], 
+			msg_len);
+	}
+	hex_dump ((char*)pmsg, msg_len);
+
+} /* end kiss_debug_print */
+
+
+/* end kiss_frame.c */
diff --git a/kiss_frame.h b/kiss_frame.h
new file mode 100755
index 0000000..4ecd0a4
--- /dev/null
+++ b/kiss_frame.h
@@ -0,0 +1,47 @@
+
+/* kiss_frame.h */
+
+
+/*
+ * Special characters used by SLIP protocol.
+ */
+
+#define FEND 0xC0
+#define FESC 0xDB
+#define TFEND 0xDC
+#define TFESC 0xDD
+
+
+
+enum kiss_state_e {
+	KS_SEARCHING,		/* Looking for FEND to start KISS frame. */
+	KS_COLLECTING,		/* In process of collecting KISS frame. */
+	KS_ESCAPE };		/* FESC found in frame. */
+
+#define MAX_KISS_LEN 2048	/* Spec calls for at least 1024. */
+
+#define MAX_NOISE_LEN 100
+
+typedef struct kiss_frame_s {
+	
+	enum kiss_state_e state;
+
+	unsigned char kiss_msg[MAX_KISS_LEN];
+	int kiss_len;
+
+	unsigned char noise[MAX_NOISE_LEN];
+	int noise_len;
+
+} kiss_frame_t;
+
+
+
+int kiss_frame (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int)); 
+ 
+void kiss_process_msg (kiss_frame_t *kf, int debug);
+
+typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t;
+
+void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len);
+
+/* end kiss_frame.h */
\ No newline at end of file
diff --git a/kissnet.c b/kissnet.c
new file mode 100755
index 0000000..91712ee
--- /dev/null
+++ b/kissnet.c
@@ -0,0 +1,671 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011-2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      kissnet.c
+ *
+ * Purpose:   	Provide service to other applications via KISS protocol via TCP socket.
+ *		
+ * Input:	
+ *
+ * Outputs:	  
+ *
+ * Description:	This provides a TCP socket for communication with a client application.
+ *
+ *		It implements the KISS TNS protocol as described in:
+ *		http://www.ka9q.net/papers/kiss.html
+ *
+ * 		Briefly, a frame is composed of 
+ *
+ *			* FEND (0xC0)
+ *			* Contents - with special escape sequences so a 0xc0
+ *				byte in the data is not taken as end of frame.
+ *				as part of the data.
+ *			* FEND
+ *
+ *		The first byte of the frame contains:
+ *	
+ *			* port number in upper nybble.
+ *			* command in lower nybble.
+ *
+ *	
+ *		Commands from application recognized:
+ *
+ *			0	Data Frame	AX.25 frame in raw format.
+ *
+ *			1	TXDELAY		See explanation in xmit.c.
+ *
+ *			2	Persistence	"	"
+ *
+ *			3 	SlotTime	"	"
+ *
+ *			4	TXtail		"	"
+ *						Spec says it is obsolete but Xastir
+ *						sends it and we respect it.
+ *
+ *			5	FullDuplex	Ignored.  Always full duplex.
+ *		
+ *			6	SetHardware	TNC specific.  Ignored.
+ *			
+ *			FF	Return		Exit KISS mode.  Ignored.
+ *
+ *
+ *		Messages sent to client application:
+ *
+ *			0	Data Frame	Received AX.25 frame in raw format.
+ *
+ *
+ *		
+ *
+ * References:	Getting Started with Winsock
+ *		http://msdn.microsoft.com/en-us/library/windows/desktop/bb530742(v=vs.85).aspx
+ *
+ * Future:	Originally we had:
+ *			KISS over serial port.
+ *			AGW over socket.
+ *		This is the two of them munged together and we end up with duplicate code.
+ *		It would have been better to separate out the transport and application layers.
+ *		Maybe someday.
+ *
+ *---------------------------------------------------------------*/
+
+
+/*
+ * Native Windows:	Use the Winsock interface.
+ * Linux:		Use the BSD socket interface.
+ * Cygwin:		Can use either one.
+ */
+
+
+#if __WIN32__
+#include <winsock2.h>
+#define _WIN32_WINNT 0x0501
+#include <ws2tcpip.h>
+#else 
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <string.h>
+
+
+#include "direwolf.h"
+#include "tq.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "audio.h"
+#include "kissnet.h"
+#include "kiss_frame.h"
+#include "xmit.h"
+
+
+static kiss_frame_t kf;		/* Accumulated KISS frame and state of decoder. */
+
+
+static int client_sock;		/* File descriptor for socket for */
+				/* communication with client application. */
+				/* Set to -1 if not connected. */
+				/* (Don't use SOCKET type because it is unsigned.) */
+
+static int num_channels;	/* Number of radio ports. */
+
+
+static void * connect_listen_thread (void *arg);
+static void * kissnet_listen_thread (void *arg);
+
+
+
+static int kiss_debug = 0;		/* Print information flowing from and to client. */
+
+void kiss_net_set_debug (int n) 
+{	
+	kiss_debug = n;
+}
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        kissnet_init
+ *
+ * Purpose:     Set up a server to listen for connection requests from
+ *		an application such as Xastir or APRSIS32.
+ *
+ * Inputs:	mc->kiss_port	- TCP port for server.
+ *				  Main program has default of 8000 but allows
+ *				  an alternative to be specified on the command line
+ *
+ * Outputs:	
+ *
+ * Description:	This starts two threads:
+ *		  *  to listen for a connection from client app.
+ *		  *  to listen for commands from client app.
+ *		so the main application doesn't block while we wait for these.
+ *
+ *--------------------------------------------------------------------*/
+
+
+void kissnet_init (struct misc_config_s *mc)
+{
+#if __WIN32__
+	HANDLE connect_listen_th;
+	HANDLE cmd_listen_th;
+#else
+	pthread_t connect_listen_tid;
+	pthread_t cmd_listen_tid;
+#endif
+	int e;
+	int kiss_port = mc->kiss_port;
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("kissnet_init ( %d )\n", kiss_port);
+#endif
+
+	memset (&kf, 0, sizeof(kf));
+	
+	client_sock = -1;
+	num_channels = mc->num_channels;
+
+/*
+ * This waits for a client to connect and sets client_sock.
+ */
+#if __WIN32__
+	connect_listen_th = _beginthreadex (NULL, 0, connect_listen_thread, (void *)kiss_port, 0, NULL);
+	if (connect_listen_th == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not create KISS socket connect listening thread\n");
+	  return;
+	}
+#else
+	e = pthread_create (&connect_listen_tid, NULL, connect_listen_thread, (void *)(long)kiss_port);
+	if (e != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror("Could not create KISS socket connect listening thread");
+	  return;
+	}
+#endif
+
+/*
+ * This reads messages from client when client_sock is valid.
+ */
+#if __WIN32__
+	cmd_listen_th = _beginthreadex (NULL, 0, kissnet_listen_thread, NULL, 0, NULL);
+	if (cmd_listen_th == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not create KISS socket command listening thread\n");
+	  return;
+	}
+#else
+	e = pthread_create (&cmd_listen_tid, NULL, kissnet_listen_thread, NULL);
+	if (e != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror("Could not create KISS socket command listening thread");
+	  return;
+	}
+#endif
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        connect_listen_thread
+ *
+ * Purpose:     Wait for a connection request from an application.
+ *
+ * Inputs:	arg		- TCP port for server.
+ *				  Main program has default of 8001 but allows
+ *				  an alternative to be specified on the command line
+ *
+ * Outputs:	client_sock	- File descriptor for communicating with client app.
+ *
+ * Description:	Wait for connection request from client and establish
+ *		communication.
+ *		Note that the client can go away and come back again and
+ *		re-establish communication without restarting this application.
+ *
+ *--------------------------------------------------------------------*/
+
+static void * connect_listen_thread (void *arg)
+{
+#if __WIN32__
+
+	struct addrinfo hints;
+	struct addrinfo *ai = NULL;
+	int err;
+	char kiss_port_str[12];
+
+	SOCKET listen_sock;  
+	WSADATA wsadata;
+
+	sprintf (kiss_port_str, "%d", (int)(long)arg);
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+        dw_printf ("DEBUG: kissnet port = %d = '%s'\n", (int)(long)arg, kiss_port_str);
+#endif
+	err = WSAStartup (MAKEWORD(2,2), &wsadata);
+	if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("WSAStartup failed: %d\n", err);
+	    return (NULL);
+	}
+
+	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
+	  text_color_set(DW_COLOR_ERROR);
+          dw_printf("Could not find a usable version of Winsock.dll\n");
+          WSACleanup();
+	  //sleep (1);
+          return (NULL);
+	}
+
+	memset (&hints, 0, sizeof(hints));
+	hints.ai_family = AF_INET;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_protocol = IPPROTO_TCP;
+	hints.ai_flags = AI_PASSIVE;
+
+	err = getaddrinfo(NULL, kiss_port_str, &hints, &ai);
+	if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("getaddrinfo failed: %d\n", err);
+	    //sleep (1);
+	    WSACleanup();
+	    return (NULL);
+	}
+
+	listen_sock= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+	if (listen_sock == INVALID_SOCKET) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("connect_listen_thread: Socket creation failed, err=%d", WSAGetLastError());
+	  return (NULL);
+	}
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+    	dw_printf("Binding to port %s ... \n", kiss_port_str);
+#endif
+
+	err = bind( listen_sock, ai->ai_addr, (int)ai->ai_addrlen);
+	if (err == SOCKET_ERROR) {
+	  text_color_set(DW_COLOR_ERROR);
+          dw_printf("Bind failed with error: %d\n", WSAGetLastError());
+          freeaddrinfo(ai);
+          closesocket(listen_sock);
+          WSACleanup();
+          return (NULL);
+        }
+
+	freeaddrinfo(ai);
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+ 	dw_printf("opened KISS socket as fd (%d) on port (%s) for stream i/o\n", listen_sock, kiss_port_str );
+#endif
+
+ 	while (1) {
+  	 
+	  while (client_sock > 0) {
+	    SLEEP_SEC(1);			/* Already connected.  Try again later. */
+	  }
+
+#define QUEUE_SIZE 5
+
+	  if(listen(listen_sock,QUEUE_SIZE) == SOCKET_ERROR)
+	  {
+	    text_color_set(DW_COLOR_ERROR);
+            dw_printf("Listen failed with error: %d\n", WSAGetLastError());
+	    return (NULL);
+	  }
+	
+	  text_color_set(DW_COLOR_INFO);
+          dw_printf("Ready to accept KISS client application on port %s ...\n", kiss_port_str);
+         
+          client_sock = accept(listen_sock, NULL, NULL);
+
+	  if (client_sock == -1) {
+	    text_color_set(DW_COLOR_ERROR);
+            dw_printf("Accept failed with error: %d\n", WSAGetLastError());
+            closesocket(listen_sock);
+            WSACleanup();
+            return (NULL);
+          }
+
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf("\nConnected to KISS client application ...\n\n");
+
+ 	}
+
+#else
+
+    	struct sockaddr_in sockaddr; /* Internet socket address stuct */
+    	socklen_t sockaddr_size = sizeof(struct sockaddr_in);
+	int kiss_port = (int)(long)arg;
+	int listen_sock;  
+
+	listen_sock= socket(AF_INET,SOCK_STREAM,0);
+	if (listen_sock == -1) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror ("connect_listen_thread: Socket creation failed");
+	  return (NULL);
+	}
+
+    	sockaddr.sin_addr.s_addr = INADDR_ANY;
+    	sockaddr.sin_port = htons(kiss_port);
+    	sockaddr.sin_family = AF_INET;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+    	dw_printf("Binding to port %d ... \n", kiss_port);
+#endif
+
+        if (bind(listen_sock,(struct sockaddr*)&sockaddr,sizeof(sockaddr))  == -1) {
+	  text_color_set(DW_COLOR_ERROR);
+    	  perror ("connect_listen_thread: Bind failed");
+          return (NULL);
+	}
+
+	getsockname( listen_sock, (struct sockaddr *)(&sockaddr), &sockaddr_size);
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+ 	dw_printf("opened KISS socket as fd (%d) on port (%d) for stream i/o\n", listen_sock, ntohs(sockaddr.sin_port) );
+#endif
+
+ 	while (1) {
+  	 
+	  while (client_sock > 0) {
+	    SLEEP_SEC(1);			/* Already connected.  Try again later. */
+	  }
+
+#define QUEUE_SIZE 5
+
+	  if(listen(listen_sock,QUEUE_SIZE) == -1)
+	  {
+	    text_color_set(DW_COLOR_ERROR);
+	    perror ("connect_listen_thread: Listen failed");
+	    return (NULL);
+	  }
+	
+	  text_color_set(DW_COLOR_INFO);
+          dw_printf("Ready to accept KISS client application on port %d ...\n", kiss_port);
+         
+          client_sock = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size);
+
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf("\nConnected to KISS client application ...\n\n");
+
+ 	}
+#endif
+}
+
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        kissnet_send_rec_packet
+ *
+ * Purpose:     Send a received packet to the client app.
+ *
+ * Inputs:	chan		- Channel number where packet was received.
+ *				  0 = first, 1 = second if any.
+ *
+ *		fbuf		- Address of raw received frame buffer
+ *				  or a text string.
+ *
+ *		flen		- Number of bytes for AX.25 frame.
+ *				  or -1 for a text string.
+ *		
+ *
+ * Description:	Send message to client if connected.
+ *		Disconnect from client, and notify user, if any error.
+ *
+ *--------------------------------------------------------------------*/
+
+
+void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen)
+{
+	unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN];
+	int kiss_len;
+	int j;
+	int err;
+
+
+	if (client_sock == -1) {
+	  return;
+	}
+	if (flen < 0) {
+	  flen = strlen((char*)fbuf);
+	  if (kiss_debug) {
+	    kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
+	  }
+	  strcpy ((char *)kiss_buff, (char *)fbuf);
+	  kiss_len = strlen((char *)kiss_buff);
+	}
+	else {
+
+	  kiss_len = 0;
+	  kiss_buff[kiss_len++] = FEND;
+	  kiss_buff[kiss_len++] = chan << 4;
+
+	  for (j=0; j<flen; j++) {
+
+	    if (fbuf[j] == FEND) {
+	      kiss_buff[kiss_len++] = FESC;
+	      kiss_buff[kiss_len++] = TFEND;
+	    }
+	    else if (fbuf[j] == FESC) {
+	      kiss_buff[kiss_len++] = FESC;
+	      kiss_buff[kiss_len++] = TFESC;
+	    }
+	    else {
+	      kiss_buff[kiss_len++] = fbuf[j];
+	    }
+	    assert (kiss_len < sizeof (kiss_buff));
+	  }
+	  kiss_buff[kiss_len++] = FEND;
+
+	  /* Bug: This has the escapes but not the surrounding FENDs. */
+
+	  if (kiss_debug) {
+	    kiss_debug_print (TO_CLIENT, NULL, kiss_buff+1, kiss_len-2);
+	  }
+	}
+
+#if __WIN32__	
+        err = send (client_sock, (char*)kiss_buff, kiss_len, 0);
+	if (err == SOCKET_ERROR)
+	{
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("\nError %d sending message to KISS client application.  Closing connection.\n\n", WSAGetLastError());
+	  closesocket (client_sock);
+	  client_sock = -1;
+	  WSACleanup();
+	}
+#else
+        err = write (client_sock, kiss_buff, kiss_len);
+	if (err <= 0)
+	{
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("\nError sending message to KISS client application.  Closing connection.\n\n");
+	  close (client_sock);
+	  client_sock = -1;    
+	}
+#endif
+	
+} /* end kissnet_send_rec_packet */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        read_from_socket
+ *
+ * Purpose:     Read from socket until we have desired number of bytes.
+ *
+ * Inputs:	fd		- file descriptor.
+ *		ptr		- address where data should be placed.
+ *		len		- desired number of bytes.
+ *
+ * Description:	Just a wrapper for the "read" system call but it should
+ *		never return fewer than the desired number of bytes.
+ *
+ * 		Not really needed for KISS because we are dealing with
+ *		a stream of bytes rather than message blocks.
+ *
+ *--------------------------------------------------------------------*/
+
+static int read_from_socket (int fd, char *ptr, int len)
+{
+	int got_bytes = 0;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("read_from_socket (%d, %p, %d)\n", fd, ptr, len);
+#endif
+	while (got_bytes < len) {
+	  int n;
+
+#if __WIN32__
+
+//TODO: any flags for send/recv?
+
+	  n = recv (fd, ptr + got_bytes, len - got_bytes, 0);
+#else
+	  n = read (fd, ptr + got_bytes, len - got_bytes);
+#endif
+
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("read_from_socket: n = %d\n", n);
+#endif
+	  if (n <= 0) {
+	    return (n);
+	  }
+
+	  got_bytes += n;
+	}
+	assert (got_bytes >= 0 && got_bytes <= len);
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("read_from_socket: return %d\n", got_bytes);
+#endif
+	return (got_bytes);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        kissnet_listen_thread
+ *
+ * Purpose:     Wait for KISS messages from an application.
+ *
+ * Inputs:	arg		- Not used.
+ *
+ * Outputs:	client_sock	- File descriptor for communicating with client app.
+ *
+ * Description:	Process messages from the client application.
+ *		Note that the client can go away and come back again and
+ *		re-establish communication without restarting this application.
+ *
+ *--------------------------------------------------------------------*/
+
+
+/* Return one byte (value 0 - 255) */
+
+
+static int kiss_get (void)
+{
+	unsigned char ch;
+	int n;
+
+	while (1) {
+
+	  while (client_sock <= 0) {
+	    SLEEP_SEC(1);			/* Not connected.  Try again later. */
+	  }
+
+	  /* Just get one byte at a time. */
+
+	  n = read_from_socket (client_sock, (char *)(&ch), 1);
+
+	  if (n == 1) {
+#if DEBUG9
+	    dw_printf (log_fp, "%02x %c %c", ch, 
+			isprint(ch) ? ch : '.' , 
+			(isupper(ch>>1) || isdigit(ch>>1) || (ch>>1) == ' ') ? (ch>>1) : '.');
+	    if (ch == FEND) fprintf (log_fp, "  FEND");
+	    if (ch == FESC) fprintf (log_fp, "  FESC");
+	    if (ch == TFEND) fprintf (log_fp, "  TFEND");
+	    if (ch == TFESC) fprintf (log_fp, "  TFESC");
+	    if (ch == '\r') fprintf (log_fp, "  CR");
+	    if (ch == '\n') fprintf (log_fp, "  LF");
+	    fprintf (log_fp, "\n");
+	    if (ch == FEND) fflush (log_fp);
+#endif
+	    return(ch);	
+	  }
+
+          text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("\nError reading KISS byte from clent application.  Closing connection.\n\n");
+#if __WIN32__
+	  closesocket (client_sock);
+#else
+	  close (client_sock);
+#endif
+	  client_sock = -1;
+	}
+}
+
+
+
+static void * kissnet_listen_thread (void *arg)
+{
+	unsigned char ch;
+			
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("kissnet_listen_thread ( socket = %d )\n", client_sock);
+#endif
+
+	while (1) {
+	  ch = kiss_get();
+
+	  if (kiss_frame (&kf, ch, kiss_debug, kissnet_send_rec_packet)) { 
+	    kiss_process_msg (&kf, kiss_debug);
+	  }
+	}  /* while (1) */
+
+	return (NULL);	/* to suppress compiler warning. */
+
+} /* end kissnet_listen_thread */
+
+/* end kissnet.c */
diff --git a/kissnet.h b/kissnet.h
new file mode 100755
index 0000000..2b4f9b9
--- /dev/null
+++ b/kissnet.h
@@ -0,0 +1,21 @@
+
+/* 
+ * Name:	kissnet.h
+ */
+
+
+#include "ax25_pad.h"		/* for packet_t */
+
+#include "config.h"
+
+
+
+
+void kissnet_init (struct misc_config_s *misc_config);
+
+void kissnet_send_rec_packet (int chan, unsigned char *fbuf,  int flen);
+
+void kiss_net_set_debug (int n);
+
+
+/* end kissnet.h */
diff --git a/latlong.c b/latlong.c
new file mode 100755
index 0000000..a31adb6
--- /dev/null
+++ b/latlong.c
@@ -0,0 +1,281 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      latlong.c
+ *
+ * Purpose:   	Various functions for dealing with latitude and longitude.
+ *		
+ * Description: Originally, these were scattered around in many places.
+ *		Over time they might all be gathered into one place
+ *		for consistency, reuse, and easier maintenance.
+ *
+ *---------------------------------------------------------------*/
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+#include <assert.h>
+
+#include "direwolf.h"
+#include "latlong.h"
+#include "textcolor.h"
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        latitude_to_str
+ *
+ * Purpose:     Convert numeric latitude to string for transmission.
+ *
+ * Inputs:      dlat		- Floating point degrees.
+ * 		ambiguity	- If 1, 2, 3, or 4, blank out that many trailing digits.
+ *
+ * Outputs:	slat		- String in format ddmm.mm[NS]
+ *
+ * Returns:     None
+ *
+ *----------------------------------------------------------------*/
+
+void latitude_to_str (double dlat, int ambiguity, char *slat)
+{
+	char hemi;	/* Hemisphere: N or S */
+	int ideg;	/* whole number of degrees. */
+	double dmin;	/* Minutes after removing degrees. */
+	char smin[8];	/* Minutes in format mm.mm */
+	
+	if (dlat < -90.) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Latitude is less than -90.  Changing to -90.n");
+	  dlat = -90.;
+	}
+	if (dlat > 90.) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Latitude is greater than 90.  Changing to 90.n");
+	  dlat = 90.;
+	}
+
+	if (dlat < 0) {
+	  dlat = (- dlat);
+	  hemi = 'S';
+	}
+	else {
+	  hemi = 'N';
+	}
+
+	ideg = (int)dlat;
+	dmin = (dlat - ideg) * 60.;
+
+	sprintf (smin, "%05.2f", dmin);
+	/* Due to roundoff, 59.9999 could come out as "60.00" */
+	if (smin[0] == '6') {
+	  smin[0] = '0';
+	  ideg++;
+	}
+
+	sprintf (slat, "%02d%s%c", ideg, smin, hemi);
+
+	if (ambiguity >= 1) {
+	  slat[6] = ' ';
+	  if (ambiguity >= 2) {
+	    slat[5] = ' ';
+	    if (ambiguity >= 3) {
+	      slat[3] = ' ';
+	      if (ambiguity >= 4) {
+	        slat[2] = ' ';
+	      }
+	    }
+	  }
+	}
+
+} /* end latitude_to_str */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        longitude_to_str
+ *
+ * Purpose:     Convert numeric longitude to string for transmission.
+ *
+ * Inputs:      dlong		- Floating point degrees.
+ * 		ambiguity	- If 1, 2, 3, or 4, blank out that many trailing digits.
+ *
+ * Outputs:	slat		- String in format dddmm.mm[NS]
+ *
+ * Returns:     None
+ *
+ *----------------------------------------------------------------*/
+
+void longitude_to_str (double dlong, int ambiguity, char *slong)
+{
+	char hemi;	/* Hemisphere: N or S */
+	int ideg;	/* whole number of degrees. */
+	double dmin;	/* Minutes after removing degrees. */
+	char smin[8];	/* Minutes in format mm.mm */
+	
+	if (dlong < -180.) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Longitude is less than -180.  Changing to -180.n");
+	  dlong = -180.;
+	}
+	if (dlong > 180.) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Longitude is greater than 180.  Changing to 180.n");
+	  dlong = 180.;
+	}
+
+	if (dlong < 0) {
+	  dlong = (- dlong);
+	  hemi = 'W';
+	}
+	else {
+	  hemi = 'E';
+	}
+
+	ideg = (int)dlong;
+	dmin = (dlong - ideg) * 60.;
+
+	sprintf (smin, "%05.2f", dmin);
+	/* Due to roundoff, 59.9999 could come out as "60.00" */
+	if (smin[0] == '6') {
+	  smin[0] = '0';
+	  ideg++;
+	}
+
+	sprintf (slong, "%03d%s%c", ideg, smin, hemi);
+/*
+ * The spec says position ambiguity in latitude also
+ * applies to longitude automatically.  
+ * Blanking longitude digits is not necessary but I do it
+ * because it makes things clearer.
+ */
+	if (ambiguity >= 1) {
+	  slong[7] = ' ';
+	  if (ambiguity >= 2) {
+	    slong[6] = ' ';
+	    if (ambiguity >= 3) {
+	      slong[4] = ' ';
+	      if (ambiguity >= 4) {
+	        slong[3] = ' ';
+	      }
+	    }
+	  }
+	}
+
+} /* end longitude_to_str */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        latitude_to_comp_str
+ *
+ * Purpose:     Convert numeric latitude to compressed string for transmission.
+ *
+ * Inputs:      dlat		- Floating point degrees.
+ *
+ * Outputs:	slat		- String in format yyyy.
+ *
+ *----------------------------------------------------------------*/
+
+void latitude_to_comp_str (double dlat, char *clat)
+{
+	int y, y0, y1, y2, y3;
+
+	if (dlat < -90.) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Latitude is less than -90.  Changing to -90.n");
+	  dlat = -90.;
+	}
+	if (dlat > 90.) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Latitude is greater than 90.  Changing to 90.n");
+	  dlat = 90.;
+	}
+
+	y = (int)round(380926. * (90. - dlat));
+	
+	y0 = y / (91*91*91);
+	y -= y0 * (91*91*91);
+
+	y1 = y / (91*91);
+	y -= y1 * (91*91);
+
+	y2 = y / (91);
+	y -= y2 * (91);
+
+	y3 = y;
+
+	clat[0] = y0 + 33;
+	clat[1] = y1 + 33;
+	clat[2] = y2 + 33;
+	clat[3] = y3 + 33;
+}
+
+/*------------------------------------------------------------------
+ *
+ * Name:        longitude_to_comp_str
+ *
+ * Purpose:     Convert numeric longitude to compressed string for transmission.
+ *
+ * Inputs:      dlong		- Floating point degrees.
+ *
+ * Outputs:	slat		- String in format xxxx.
+ *
+ *----------------------------------------------------------------*/
+
+void longitude_to_comp_str (double dlong, char *clon)
+{
+	int x, x0, x1, x2, x3;
+
+	if (dlong < -180.) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Longitude is less than -180.  Changing to -180.n");
+	  dlong = -180.;
+	}
+	if (dlong > 180.) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Longitude is greater than 180.  Changing to 180.n");
+	  dlong = 180.;
+	}
+
+	x = (int)round(190463. * (180. + dlong));
+	
+	x0 = x / (91*91*91);
+	x -= x0 * (91*91*91);
+
+	x1 = x / (91*91);
+	x -= x1 * (91*91);
+
+	x2 = x / (91);
+	x -= x2 * (91);
+
+	x3 = x;
+
+	clon[0] = x0 + 33;
+	clon[1] = x1 + 33;
+	clon[2] = x2 + 33;
+	clon[3] = x3 + 33;
+}
diff --git a/latlong.h b/latlong.h
new file mode 100755
index 0000000..c55b590
--- /dev/null
+++ b/latlong.h
@@ -0,0 +1,13 @@
+
+/* latlong.h */
+
+
+/* Use this value for unknown latitude/longitude or other values. */
+
+#define G_UNKNOWN (-999999)
+
+
+void latitude_to_str (double dlat, int ambiguity, char *slat);
+void longitude_to_str (double dlong, int ambiguity, char *slong);
+void latitude_to_comp_str (double dlat, char *clat);
+void longitude_to_comp_str (double dlon, char *clon);
diff --git a/ll2utm.c b/ll2utm.c
new file mode 100755
index 0000000..a1858c9
--- /dev/null
+++ b/ll2utm.c
@@ -0,0 +1,55 @@
+/* Latitude / Longitude to UTM conversion */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "LatLong-UTMconversion.h"
+
+
+static void usage();
+
+
+void main (int argc, char *argv[]) 
+{
+	double easting;
+	double northing;
+	double lat, lon;
+	char zone[8];
+
+	if (argc != 3) usage();
+
+
+	lat = atof(argv[1]);
+	if (lat < -90 || lat > 90) {
+	  fprintf (stderr, "Latitude value is out of range.\n\n");
+	  usage();
+	}
+
+	lon = atof(argv[2]);
+	if (lon < -180 || lon > 180) {
+	  fprintf (stderr, "Longitude value is out of range.\n\n");
+	  usage();
+	}
+
+	LLtoUTM (WSG84, lat, lon, &northing, &easting, zone);
+
+	printf ("zone = %s, easting = %.0f, northing = %.0f\n", zone, easting, northing);
+}
+
+
+static void usage (void)
+{
+	fprintf (stderr, "Latitude / Longitude to UTM conversion\n");
+	fprintf (stderr, "\n");
+	fprintf (stderr, "Usage:\n");
+	fprintf (stderr, "\tll2utm  latitude  longitude\n");
+	fprintf (stderr, "\n");
+	fprintf (stderr, "where,\n");
+	fprintf (stderr, "\tLatitude and longitude are in decimal degrees.\n");
+	fprintf (stderr, "\t   Use negative for south or west.\n");
+	fprintf (stderr, "\n");
+	fprintf (stderr, "Example:\n");
+	fprintf (stderr, "\tll2utm 42.662139 -71.365553\n");
+
+	exit (1);
+}
\ No newline at end of file
diff --git a/misc/README-dire-wolf.txt b/misc/README-dire-wolf.txt
new file mode 100755
index 0000000..a6373bc
--- /dev/null
+++ b/misc/README-dire-wolf.txt
@@ -0,0 +1,8 @@
+These are part of the standard C library for Linux and Cygwin.
+For the Windows version we need to include our own copy.
+
+They were copied from Cygwin source:
+
+	/usr/src/cygwin-1.7.10-1/newlib/libc/string/strsep.c
+	/usr/src/cygwin-1.7.10-1/newlib/libc/string/strtok_r.c
+
diff --git a/misc/strcasestr.c b/misc/strcasestr.c
new file mode 100755
index 0000000..6cadc2a
--- /dev/null
+++ b/misc/strcasestr.c
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+//#include <sys/cdefs.h>
+
+#include <ctype.h>
+#include <string.h>
+
+/*
+ * Find the first occurrence of find in s, ignore case.
+ */
+char *
+strcasestr(s, find)
+	const char *s, *find;
+{
+	char c, sc;
+	size_t len;
+
+	if ((c = *find++) != 0) {
+		c = tolower((unsigned char)c);
+		len = strlen(find);
+		do {
+			do {
+				if ((sc = *s++) == 0)
+					return (NULL);
+			} while ((char)tolower((unsigned char)sc) != c);
+		} while (strncasecmp(s, find, len) != 0);
+		s--;
+	}
+	return ((char *)s);
+}
diff --git a/misc/strsep.c b/misc/strsep.c
new file mode 100755
index 0000000..37181b0
--- /dev/null
+++ b/misc/strsep.c
@@ -0,0 +1,22 @@
+/* BSD strsep function */
+
+/* Copyright 2002, Red Hat Inc. */
+
+/* undef STRICT_ANSI so that strsep prototype will be defined */
+#undef  __STRICT_ANSI__
+#include <string.h>
+//#include <_ansi.h>
+//#include <reent.h>
+
+#define _DEFUN(name,arglist,args) name(args)
+#define _AND ,
+
+extern char *__strtok_r (char *, const char *, char **, int);
+
+char *
+_DEFUN (strsep, (source_ptr, delim),
+	register char **source_ptr _AND
+	register const char *delim)
+{
+	return __strtok_r (*source_ptr, delim, source_ptr, 0);
+}
diff --git a/misc/strtok_r.c b/misc/strtok_r.c
new file mode 100755
index 0000000..d56a4e4
--- /dev/null
+++ b/misc/strtok_r.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#define _DEFUN(name,arglist,args) name(args)
+#define _AND ,
+
+char *
+_DEFUN (__strtok_r, (s, delim, lasts, skip_leading_delim),
+	register char *s _AND
+	register const char *delim _AND
+	char **lasts _AND
+	int skip_leading_delim)
+{
+	register char *spanp;
+	register int c, sc;
+	char *tok;
+
+
+	if (s == NULL && (s = *lasts) == NULL)
+		return (NULL);
+
+	/*
+	 * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+	 */
+cont:
+	c = *s++;
+	for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
+		if (c == sc) {
+			if (skip_leading_delim) {
+				goto cont;
+			}
+			else {
+				*lasts = s;
+				s[-1] = 0;
+				return (s - 1);
+			}
+		}
+	}
+
+	if (c == 0) {		/* no non-delimiter characters */
+		*lasts = NULL;
+		return (NULL);
+	}
+	tok = s - 1;
+
+	/*
+	 * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+	 * Note that delim must have one NUL; we stop if we see that, too.
+	 */
+	for (;;) {
+		c = *s++;
+		spanp = (char *)delim;
+		do {
+			if ((sc = *spanp++) == c) {
+				if (c == 0)
+					s = NULL;
+				else
+					s[-1] = 0;
+				*lasts = s;
+				return (tok);
+			}
+		} while (sc != 0);
+	}
+	/* NOTREACHED */
+}
+
+char *
+_DEFUN (strtok_r, (s, delim, lasts),
+	register char *s _AND
+	register const char *delim _AND
+	char **lasts)
+{
+	return __strtok_r (s, delim, lasts, 1);
+}
diff --git a/morse.c b/morse.c
new file mode 100755
index 0000000..df67e3d
--- /dev/null
+++ b/morse.c
@@ -0,0 +1,381 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      morse.c
+ *
+ * Purpose:   	Generate audio for morse code.
+ *		
+ * Description:	
+ *
+ * Reference:	
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/time.h>
+
+#if __WIN32__
+#include <windows.h>
+#else
+#include <sys/termios.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#endif
+
+#include "direwolf.h"
+#include "textcolor.h"
+#include "audio.h"
+#include "ptt.h"
+
+
+#define WPM 10
+#define TIME_UNITS_TO_MS(tu,wpm)  (((tu)*1200)/(wpm))
+
+
+// TODO : should be in .h file.
+
+/*
+ * Delay from PTT on to start of first character.
+ * Currently the only anticipated use for this is 
+ * APRStt responses.  In this case, we want an adequate
+ * delay for someone to press the # button, release 
+ * the PTT button, and start listening for a response.
+ */
+#define MORSE_TXDELAY_MS  1500
+
+/*
+ * Delay from end of last character to PTT off.
+ * Avoid chopping off the last element.
+ */
+#define MORSE_TXTAIL_MS   200
+
+
+static const struct morse_s {
+	char ch;
+	char enc[7];
+} morse[] = {
+	{ 'A', ".-" },
+	{ 'B', "-..." },
+	{ 'C', "-.-." },
+	{ 'D', "-.." },
+	{ 'E', "." },
+	{ 'F', "..-." },
+	{ 'G', "--." },
+	{ 'H', "...." },
+	{ 'I', "." },
+	{ 'J', ".---" },
+	{ 'K', "-.-" },
+	{ 'L', ".-.." },
+	{ 'M', "--" },
+	{ 'N', "-." },
+	{ 'O', "---" },
+	{ 'P', ".--." },
+	{ 'Q', "--.-" },
+	{ 'R', ".-." },
+	{ 'S', "..." },
+	{ 'T', "-" },
+	{ 'U', "..-" },
+	{ 'V', "...-" },
+	{ 'W', ".--" },
+	{ 'X', "-..-" },
+	{ 'Y', "-.--" },
+	{ 'Z', "--.." },
+	{ '1', ".----" },
+	{ '2', "..---" },
+	{ '3', "...--" },
+	{ '4', "....-" },
+	{ '5', "....." },
+	{ '6', "-...." },
+	{ '7', "--..." },
+	{ '8', "---.." },
+	{ '9', "----." },
+	{ '0', "-----" },
+	{ '-', "-...-" },
+	{ '.', ".-.-.-" },
+	{ ',', "--..--" },
+	{ '?', "..--.." },
+	{ '/', "-..-." }
+};
+
+#define NUM_MORSE (sizeof(morse) / sizeof(struct morse_s))
+
+static void morse_tone (int tu);
+static void morse_quiet (int tu);
+static int morse_lookup (int ch);
+static int morse_units_ch (int ch);
+static int morse_units_str (char *str);
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        morse_send
+ *
+ * Purpose:    	Given a string, generate appropriate lengths of
+ *		tone and silence.
+ *
+ * Inputs:	chan	- Radio channel number.
+ *		str	- Character string to send.
+ *		wpm	- Speed in words per minute.
+ *		txdelay	- Delay (ms) from PTT to first character.
+ *		txtail	- Delay (ms) from last character to PTT off.	
+ *		
+ *
+ * Returns:	Total number of milliseconds to activate PTT.
+ *		This includes delays before the first character
+ *		and after the last to avoid chopping off part of it.
+ *
+ * Description:	xmit_thread calls this instead of the usual hdlc_send
+ *		when we have a special packet that means send morse
+ *		code.
+ *
+ *--------------------------------------------------------------------*/
+
+int morse_send (int chan, char *str, int wpm, int txdelay, int txtail)
+{
+	int time_units;
+	char *p;
+
+	time_units = 0;
+	for (p = str; *p != '\0'; p++) {
+	  int i;
+
+	  i = morse_lookup (*p);
+	  if (i >= 0) {
+	    const char *e;
+
+	    for (e = morse[i].enc; *e != '\0'; e++) {
+	      if (*e == '.') {
+	        morse_tone (1);
+	        time_units++;
+	      }
+	      else {
+	        morse_tone (3);
+	        time_units += 3;
+	      }
+	      if (e[1] != '\0') {
+	        morse_quiet (1);
+	        time_units++;
+	      }
+	    }
+	  }
+	  else {
+	    morse_quiet (1);
+	    time_units++;
+	  }
+	  if (p[1] != '\0') {
+	    morse_quiet (3);
+	    time_units += 3;
+	  }
+	}
+
+	if (time_units != morse_units_str(str)) {
+	  dw_printf ("morse: Internal error.  Inconsistent length, %d vs. %d calculated.\n", 
+		time_units, morse_units_str(str));
+	}
+
+
+	return (txdelay +
+		TIME_UNITS_TO_MS(time_units, wpm) +
+		txtail);
+
+}  /* end morse_send */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        morse_tone
+ *
+ * Purpose:    	Generate tone for specified number of time units.
+ *
+ * Inputs:	tu	- Number of time units.
+ *
+ *--------------------------------------------------------------------*/
+
+static void morse_tone (int tu) {
+	int num_cycles;
+	int n;
+
+	for (n=0; n<tu; n++) {
+	  dw_printf ("#");
+	}
+
+	
+
+} /* end morse_tone */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        morse_quiet
+ *
+ * Purpose:    	Generate silence for specified number of time units.
+ *
+ * Inputs:	tu	- Number of time units.
+ *
+ *--------------------------------------------------------------------*/
+
+static void morse_quiet (int tu) {
+	int n;
+
+	for (n=0; n<tu; n++) {
+	  dw_printf (".");
+	}
+
+} /* end morse_quiet */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        morse_lookup
+ *
+ * Purpose:    	Given a character, find index in table above.
+ *
+ * Inputs:	ch
+ *
+ * Returns:	Index into table above or -1 if not found.
+ *		Notice that space is not in the table.
+ *		Any unusual character, that is not in the table, 
+ *		ends up being treated like space.
+ *
+ *--------------------------------------------------------------------*/
+
+static int morse_lookup (int ch)
+{
+	int i;
+
+	if (islower(ch)) {
+	  ch = toupper(ch);
+	}
+
+	for (i=0; i<NUM_MORSE; i++) {
+	  if (ch == morse[i].ch) {
+	    return (i);
+	  }
+	}
+	return (-1);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        morse_units_ch
+ *
+ * Purpose:    	Find number of time units for a character.
+ *
+ * Inputs:	ch
+ *
+ * Returns:	1 for E (.)
+ *		3 for T (-)
+ *		3 for I.= (..)
+ *		etc.
+ *	
+ *		The one unexpected result is 1 for space.  Why not 7?
+ *		When a space appears between two other characters,
+ *		we already have 3 before and after so only 1 more is needed.
+ *
+ *--------------------------------------------------------------------*/
+
+static int morse_units_ch (int ch)
+{
+	int i;
+	int len;
+	int k;
+	int units;
+
+	i = morse_lookup (ch);
+	if (i < 0) {
+	  return (1);	/* space or any invalid character */
+	}
+
+	
+	len = strlen(morse[i].enc);
+	units = len - 1;
+
+	for (k = 0; k < len; k++) {
+	  switch (morse[i].enc[k]) {
+	    case '.':  units++; break;
+	    case '-':  units += 3; break;
+	    default:  dw_printf ("ERROR: morse_units_ch: should not be here.\n"); break;
+	  }
+	}
+	
+	return (units); 
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        morse_units_str
+ *
+ * Purpose:    	Find number of time units for a string of characters.
+ *
+ * Inputs:	str
+ *
+ * Returns:	1 for E 	
+ *		5 for EE	(1 + 3 + 1)
+ *		9 for E E	(1 + 7 + 1)
+ *		etc.
+ *
+ *--------------------------------------------------------------------*/
+
+static int morse_units_str (char *str)
+{
+	int i;
+	int len;
+	int k;
+	int units;
+
+	len = strlen(str);
+	units = (len - 1) * 3;
+
+	for (k = 0; k < len; k++) {
+	  units += morse_units_ch(str[k]);
+	}
+	
+	return (units); 
+}
+
+
+int main (int argc, char *argv[]) {
+
+	dw_printf ("CQ DX\n");
+	morse_send (0, "CQ DX", 10, 10, 10);
+	dw_printf ("\n\n");
+
+	dw_printf ("wb2osz/9\n");
+	morse_send (0, "wb2osz/9", 10, 10, 10);
+	dw_printf ("\n\n");
+
+} 
+
+
+/* end morse.c */
+
+
+
diff --git a/multi_modem.c b/multi_modem.c
new file mode 100755
index 0000000..aae42f0
--- /dev/null
+++ b/multi_modem.c
@@ -0,0 +1,484 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:	multi_modem.c
+ *
+ * Purpose:	Use multiple modems in parallel to increase chances
+ *		of decoding less than ideal signals.
+ *
+ * Description:	The initial motivation was for HF SSB where mistuning
+ *		causes a shift in the audio frequencies.  Here, we can
+ * 		have multiple modems tuned to staggered pairs of tones
+ *		in hopes that one will be close enough.
+ *
+ *		The overall structure opens the door to other approaches
+ *		as well.  For VHF FM, the tones should always have the
+ *		right frequencies but we might want to tinker with other
+ *		modem parameters instead of using a single compromise.
+ *
+ * Originally:	The the interface application is in 3 places:
+ *
+ *		(a) Main program (direwolf.c or atest.c) calls 
+ *		    demod_init to set up modem properties and
+ *		    hdlc_rec_init for the HDLC decoders.
+ *
+ *		(b) demod_process_sample is called for each audio sample
+ *		    from the input audio stream.
+ *
+ *	   	(c) When a valid AX.25 frame is found, process_rec_frame,
+ *		    provided by the application, in direwolf.c or atest.c,
+ *		    is called.  Normally this comes from hdlc_rec.c but
+ *		    there are a couple other special cases to consider.
+ *		    It can be called from hdlc_rec2.c if it took a long 
+ *  		    time to "fix" corrupted bits.  aprs_tt.c constructs 
+ * 		    a fake packet when a touch tone message is received.
+ *
+ * New in version 0.9:
+ *
+ *		Put an extra layer in between which potentially uses
+ *		multiple modems & HDLC decoders per channel.  The tricky
+ *		part is picking the best one when there is more than one
+ *		success and discarding the rest.
+ *		
+ *------------------------------------------------------------------*/
+
+#define DIGIPEATER_C
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <sys/unistd.h>
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "multi_modem.h"
+#include "demod.h"
+#include "hdlc_rec.h"
+#include "hdlc_rec2.h"
+
+
+// Properties of the radio channels.
+
+static struct audio_s modem;
+
+
+// Candidates for further processing.
+
+static struct {
+
+	packet_t packet_p;
+	int alevel;
+	retry_t retries;
+	int age;
+	unsigned int crc;
+	int score;
+
+} candidate[MAX_CHANS][MAX_SUBCHANS];
+
+static unsigned int crc_of_last_to_app[MAX_CHANS];
+
+#define PROCESS_AFTER_BITS 2
+
+static int process_age[MAX_CHANS];
+
+static void pick_best_candidate (int chan);
+
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	multi_modem_init
+ * 
+ * Purpose:	Called at application start up to initialize appropriate
+ *		modems and HDLC decoders.
+ *
+ * Input:	Modem properties structure as filled in from the configuration file.
+ *		
+ * Outputs:	
+ *		
+ * Description:	Called once at application startup time.
+ *
+ *------------------------------------------------------------------------------*/
+
+void multi_modem_init (struct audio_s *pmodem) 
+{
+	int chan;
+
+/*
+ * Save parameters for later use.
+ */
+	memcpy (&modem, pmodem, sizeof(modem));
+
+	memset (candidate, 0, sizeof(candidate));
+
+	demod_init (pmodem);
+	hdlc_rec_init (pmodem);
+
+	for (chan=0; chan<modem.num_channels; chan++) {
+	  process_age[chan] = PROCESS_AFTER_BITS * modem.samples_per_sec / modem.baud[chan];
+	  crc_of_last_to_app[chan] = 0x12345678;
+	}
+
+}
+
+
+
+/*------------------------------------------------------------------------------
+ *
+ * Name:	multi_modem_process_sample
+ * 
+ * Purpose:	Feed the sample into the proper modem(s) for the channel.	
+ *
+ * Inputs:	chan	- Radio channel number
+ *
+ *		audio_sample 
+ *		
+ *------------------------------------------------------------------------------*/
+
+
+__attribute__((hot))
+void multi_modem_process_sample (int chan, int audio_sample) 
+{
+	int subchan;
+
+	for (subchan = 0; subchan < modem.num_subchan[chan]; subchan++) {
+	  demod_process_sample(chan, subchan, audio_sample);
+
+	  if (candidate[chan][subchan].packet_p != NULL) {
+	    candidate[chan][subchan].age++;
+	    if (candidate[chan][subchan].age > process_age[chan]) {
+	      pick_best_candidate (chan);
+	    }
+	  }  
+	}
+}
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        multi_modem_process_rec_frame
+ *
+ * Purpose:     This is called when we receive a frame with a valid 
+ *		FCS and acceptable size.
+ *
+ * Inputs:	chan	- Audio channel number, 0 or 1.
+ *		subchan	- Which modem/decoder found it.
+ *		fbuf	- Pointer to first byte in HDLC frame.
+ *		flen	- Number of bytes excluding the FCS.
+ *		alevel	- Audio level, range of 0 - 100.
+ *				(Special case, use negative to skip
+ *				 display of audio level line.
+ *				 Use -2 to indicate DTMF message.)
+ *		retries	- Level of bit correction used.
+ *
+ *
+ * Description:	Add to list of candidates.  Best one will be picked later.
+ *
+ *--------------------------------------------------------------------*/
+
+/*
+ 
+	It gets a little more complicated when we try fixing frames
+	with imperfect CRCs.   
+
+	Changing of adjacent bits is quick and done immediately.  These
+	all come in at nearly the same time.  The processing of two 
+	separated bits can take a long time and is handled in the 
+	background by another thread.  These could come in seconds later.
+
+	We need a way to remove duplicates.  I think these are the
+	two cases we need to consider.
+
+	(1) Same result as earlier no error or adjacent bit errors.
+
+		____||||_
+		0.0: ptr=00000000
+		0.1: ptr=00000000
+		0.2: ptr=00000000
+		0.3: ptr=00000000
+		0.4: ptr=009E5540, retry=0, age=295, crc=9458, score=5024
+		0.5: ptr=0082F008, retry=0, age=294, crc=9458, score=5026  ***
+		0.6: ptr=009CE560, retry=0, age=293, crc=9458, score=5026
+		0.7: ptr=009CEE08, retry=0, age=293, crc=9458, score=5024
+		0.8: ptr=00000000
+
+		___._____
+		0.0: ptr=00000000
+		0.1: ptr=00000000
+		0.2: ptr=00000000
+		0.3: ptr=009E5540, retry=4, age=295, crc=9458, score=1000  ***
+		0.4: ptr=00000000
+		0.5: ptr=00000000
+		0.6: ptr=00000000
+		0.7: ptr=00000000
+		0.8: ptr=00000000
+
+	(2) Only results from adjusting two non-adjacent bits.
+
+
+		||||||||_
+		0.0: ptr=022EBA08, retry=0, age=289, crc=5acd, score=5042
+		0.1: ptr=022EA8B8, retry=0, age=290, crc=5acd, score=5048
+		0.2: ptr=022EB160, retry=0, age=290, crc=5acd, score=5052
+		0.3: ptr=05BD0048, retry=0, age=291, crc=5acd, score=5054  ***
+		0.4: ptr=04FE0048, retry=0, age=292, crc=5acd, score=5054
+		0.5: ptr=05E10048, retry=0, age=294, crc=5acd, score=5052
+		0.6: ptr=053D0048, retry=0, age=294, crc=5acd, score=5048
+		0.7: ptr=02375558, retry=0, age=295, crc=5acd, score=5042
+		0.8: ptr=00000000
+
+		_______._
+		0.0: ptr=00000000
+		0.1: ptr=00000000
+		0.2: ptr=00000000
+		0.3: ptr=00000000
+		0.4: ptr=00000000
+		0.5: ptr=00000000
+		0.6: ptr=00000000
+		0.7: ptr=02375558, retry=4, age=295, crc=5fc5, score=1000  ***
+		0.8: ptr=00000000
+
+		________.
+		0.0: ptr=00000000
+		0.1: ptr=00000000
+		0.2: ptr=00000000
+		0.3: ptr=00000000
+		0.4: ptr=00000000
+		0.5: ptr=00000000
+		0.6: ptr=00000000
+		0.7: ptr=00000000
+		0.8: ptr=02375558, retry=4, age=295, crc=5fc5, score=1000  ***
+
+
+	These can both be covered by keepin the last CRC and dropping 
+	duplicates.  In theory we could get another frame in between with
+	a slow computer so the complete solution would be to remember more
+	than one.
+*/
+
+void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf, int flen, int alevel, retry_t retries)  
+{	
+	packet_t pp;
+
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+	pp = ax25_from_frame (fbuf, flen, alevel);
+
+	if (pp == NULL) {
+	  return;	/* oops!  why would it fail? */
+	}
+
+/*
+ * If single modem, push it thru and forget about all this foolishness.
+ */
+	if (modem.num_subchan[chan] == 1) {
+	  app_process_rec_packet (chan, subchan, pp, alevel, retries, "");
+	  return;
+	}
+
+/*
+ * Special handing for two separated bit errors.
+ * See description earlier.
+ *
+ * Not combined with others to find the best score.
+ * Either pass it along or drop if duplicate.
+ */
+
+	if (retries == RETRY_TWO_SEP) {
+	  int mycrc;
+	  char spectrum[MAX_SUBCHANS+1];
+
+	  memset (spectrum, 0, sizeof(spectrum));
+	  memset (spectrum, '_', (size_t)modem.num_subchan[chan]);
+	  spectrum[subchan] = '.';
+
+	  mycrc = ax25_m_m_crc(pp);
+
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("\n%s\n%d.%d: ptr=%p, retry=%d, age=, crc=%04x, score=  \n", 
+		spectrum, chan, subchan, pp, (int)retries, mycrc);
+#endif	   
+	  if (mycrc == crc_of_last_to_app[chan]) {
+	     /* Same as last one.  Drop it. */
+	     ax25_delete (pp);
+#if DEBUG
+	     dw_printf ("Drop duplicate.\n");
+#endif
+	     return;
+	   }
+
+#if DEBUG
+	  dw_printf ("Send the best one along.\n");
+#endif
+	  app_process_rec_packet (chan, subchan, pp, alevel, retries, spectrum);
+	  crc_of_last_to_app[chan] = mycrc;
+	  return;
+	}
+
+
+/*
+ * Otherwise, save them up for a few bit times so we can pick the best.
+ */
+	if (candidate[chan][subchan].packet_p != NULL) {
+	  /* Oops!  Didn't expect it to be there. */
+	  ax25_delete (candidate[chan][subchan].packet_p);
+	  candidate[chan][subchan].packet_p = NULL;
+	}
+
+	candidate[chan][subchan].packet_p = pp;
+	candidate[chan][subchan].alevel = alevel;
+	candidate[chan][subchan].retries = retries;
+	candidate[chan][subchan].age = 0;
+	candidate[chan][subchan].crc = ax25_m_m_crc(pp);
+}
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        pick_best_candidate
+ *
+ * Purpose:     This is called when we have one or more candidates
+ *		available for a certain amount of time.
+ *
+ * Description:	Pick the best one and send it up to the application.
+ *		Discard the others.
+ *		
+ * Rules:	We prefer one received perfectly but will settle for
+ *		one where some bits had to be flipped to get a good CRC.
+ *
+ *--------------------------------------------------------------------*/
+
+
+static void pick_best_candidate (int chan) 
+{
+	int subchan;
+	int best_subchan, best_score;
+	char spectrum[MAX_SUBCHANS+1];
+	int k;
+
+	memset (spectrum, 0, sizeof(spectrum));
+
+	for (subchan = 0; subchan < modem.num_subchan[chan]; subchan++) {
+
+	  /* Build the spectrum display. */
+
+	  if (candidate[chan][subchan].packet_p == NULL) {
+	    spectrum[subchan] = '_';
+	  }
+	  else if (candidate[chan][subchan].retries == RETRY_NONE) {
+	    spectrum[subchan] = '|';
+	  }
+	  else if (candidate[chan][subchan].retries == RETRY_SINGLE) {
+	    spectrum[subchan] = ':';
+	  }
+	  else  {
+	    spectrum[subchan] = '.';
+	  }
+
+	  /* Begining score depends on effort to get a valid frame CRC. */
+
+	  candidate[chan][subchan].score = 5000 - ((int)candidate[chan][subchan].retries * 1000);
+
+	  /* Bump it up slightly if others nearby have the same CRC. */
+	  
+	  for (k = 0; k < modem.num_subchan[chan]; k++) {
+	    if (k != subchan && candidate[chan][k].packet_p != NULL) {
+	      if (candidate[chan][k].crc == candidate[chan][subchan].crc) {
+	        candidate[chan][subchan].score += (MAX_SUBCHANS+1) - abs(subchan-k);
+	      }
+	    }
+	  }
+	}
+  
+	best_subchan = 0;
+	best_score = 0;
+
+	for (subchan = 0; subchan < modem.num_subchan[chan]; subchan++) {
+	  if (candidate[chan][subchan].packet_p != NULL) {
+	    if (candidate[chan][subchan].score > best_score) {
+	       best_score = candidate[chan][subchan].score;
+	       best_subchan = subchan;
+	    }
+	  }
+	}
+
+#if DEBUG	
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("\n%s\n", spectrum);
+
+	for (subchan = 0; subchan < modem.num_subchan[chan]; subchan++) {
+
+	  if (candidate[chan][subchan].packet_p == NULL) {
+	    dw_printf ("%d.%d: ptr=%p\n", chan, subchan,
+		candidate[chan][subchan].packet_p);
+	  }
+	  else {
+	    dw_printf ("%d.%d: ptr=%p, retry=%d, age=%3d, crc=%04x, score=%d  %s\n", chan, subchan,
+		candidate[chan][subchan].packet_p, 
+		(int)(candidate[chan][subchan].retries), 
+		candidate[chan][subchan].age,
+		candidate[chan][subchan].crc,
+		candidate[chan][subchan].score,
+		subchan == best_subchan ? "***" : "");
+	  }
+	}
+#endif
+
+/*
+ * send the best one along.
+ */
+	app_process_rec_packet (chan, best_subchan, 
+		candidate[chan][best_subchan].packet_p, 
+		candidate[chan][best_subchan].alevel, 
+		(int)(candidate[chan][best_subchan].retries), 
+		spectrum);
+        crc_of_last_to_app[chan] = candidate[chan][best_subchan].crc;
+
+	/* Someone else will delete so don't do it below. */
+	candidate[chan][best_subchan].packet_p = NULL;
+
+	/* Clear out in preparation for next time. */
+
+	for (subchan = 0; subchan < modem.num_subchan[chan]; subchan++) {
+	  if (candidate[chan][subchan].packet_p != NULL) {
+	    ax25_delete (candidate[chan][subchan].packet_p);
+	    candidate[chan][subchan].packet_p = NULL;
+	  }
+	  candidate[chan][subchan].alevel = 0;
+	  candidate[chan][subchan].retries = 0;
+	  candidate[chan][subchan].age = 0;
+	  candidate[chan][subchan].crc = 0;
+	}
+}
+
+
+/* end multi_modem.c */
diff --git a/multi_modem.h b/multi_modem.h
new file mode 100755
index 0000000..fb5ea05
--- /dev/null
+++ b/multi_modem.h
@@ -0,0 +1,20 @@
+/* multi_modem.h */
+
+#ifndef MULTI_MODEM_H
+#define MULTI_MODEM 1
+
+/* Needed for typedef retry_t. */
+#include "hdlc_rec2.h"
+
+/* Needed for struct audio_s */
+#include "audio.h"
+
+
+void multi_modem_init (struct audio_s *pmodem); 
+
+void multi_modem_process_sample (int c, int audio_sample);
+
+void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf, int flen, int level, retry_t retries);
+
+
+#endif
diff --git a/ptt.c b/ptt.c
new file mode 100755
index 0000000..01c77f3
--- /dev/null
+++ b/ptt.c
@@ -0,0 +1,690 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      ptt.c
+ *
+ * Purpose:   	Activate the push to talk (PTT) signal to turn on transmitter.
+ *		
+ * Description:	Traditionally this is done with the RTS signal of the serial port.
+ *
+ *		If we have two radio channels and only one serial port, DTR
+ *		can be used for the second channel.
+ *
+ *		If __WIN32__ is defined, we use the Windows interface.
+ *		Otherwise we use the unix version suitable for either Cygwin or Linux.
+ *
+ * Version 0.9:	Add ability to use GPIO pins on Linux.
+ *
+ * References:	http://www.robbayer.com/files/serial-win.pdf
+ *
+ *		https://www.kernel.org/doc/Documentation/gpio.txt
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/time.h>
+
+#if __WIN32__
+#include <windows.h>
+#else
+#include <sys/termios.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* So we can have more common code for fd. */
+typedef int HANDLE;
+#define INVALID_HANDLE_VALUE (-1)
+
+#endif
+
+#include "direwolf.h"
+#include "textcolor.h"
+#include "audio.h"
+#include "ptt.h"
+
+
+#if __WIN32__
+
+#define RTS_ON(fd) 	EscapeCommFunction(fd,SETRTS);
+#define RTS_OFF(fd) 	EscapeCommFunction(fd,CLRRTS);
+#define DTR_ON(fd)    	EscapeCommFunction(fd,SETDTR);
+#define DTR_OFF(fd)	EscapeCommFunction(fd,CLRDTR);
+
+#else
+
+#define RTS_ON(fd) 	{ int stuff; ioctl (fd, TIOCMGET, &stuff); stuff |= TIOCM_RTS;  ioctl (fd, TIOCMSET, &stuff); }
+#define RTS_OFF(fd) 	{ int stuff; ioctl (fd, TIOCMGET, &stuff); stuff &= ~TIOCM_RTS; ioctl (fd, TIOCMSET, &stuff); }
+#define DTR_ON(fd)    	{ int stuff; ioctl (fd, TIOCMGET, &stuff); stuff |= TIOCM_DTR;  ioctl (fd, TIOCMSET, &stuff); }
+#define DTR_OFF(fd)	{ int stuff; ioctl (fd, TIOCMGET, &stuff); stuff &= ~TIOCM_DTR;	ioctl (fd, TIOCMSET, &stuff); }
+
+#endif
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        ptt_init
+ *
+ * Purpose:    	Open serial port(s) used for PTT signals and set to proper state.
+ *
+ * Inputs:	modem		- Structure with communication parameters.
+ *
+ *
+ * Outputs:	Remember required information for future use.
+ *
+ * Description:	
+ *
+ *--------------------------------------------------------------------*/
+
+static int ptt_num_channels;
+
+static ptt_method_t ptt_method[MAX_CHANS];	/* Method for PTT signal. */
+					/* PTT_METHOD_NONE - not configured.  Could be using VOX. */
+					/* PTT_METHOD_SERIAL - serial (com) port. */
+					/* PTT_METHOD_GPIO - general purpose I/O. */
+
+static char ptt_device[MAX_CHANS][20];	/* Name of serial port device.  */
+					/* e.g. COM1 or /dev/ttyS0. */
+
+static ptt_line_t ptt_line[MAX_CHANS];	/* RTS or DTR when using serial port. */
+
+static int ptt_gpio[MAX_CHANS];		/* GPIO number.  Only used for Linux. */
+					/* Valid only when ptt_method is PTT_METHOD_GPIO. */
+		
+static int ptt_invert[MAX_CHANS];	/* Invert the signal.  */
+					/* Normally higher voltage means transmit. */
+
+static HANDLE ptt_fd[MAX_CHANS];	/* Serial port handle or fd.  */
+					/* Could be the same for two channels */	
+					/* if using both RTS and DTR. */
+
+
+
+void ptt_init (struct audio_s *p_modem)
+{
+	int ch;
+	HANDLE fd;
+#if __WIN32__
+#else
+	int using_gpio;
+#endif
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("ptt_init ( ... )\n");
+#endif
+
+/*
+ * First copy everything from p_modem to local variables
+ * so it is available for later use.
+ *
+ * Maybe all the PTT stuff should have its own structure.
+ */
+
+	ptt_num_channels = p_modem->num_channels;
+
+	assert (ptt_num_channels >= 1 && ptt_num_channels <= MAX_CHANS);
+
+	for (ch=0; ch<ptt_num_channels; ch++) {
+
+	  ptt_method[ch] = p_modem->ptt_method[ch];
+	  strcpy (ptt_device[ch], p_modem->ptt_device[ch]);
+	  ptt_line[ch] = p_modem->ptt_line[ch];
+	  ptt_gpio[ch] = p_modem->ptt_gpio[ch];
+	  ptt_invert[ch] = p_modem->ptt_invert[ch];
+	  ptt_fd[ch] = INVALID_HANDLE_VALUE;
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+          dw_printf ("ch=%d, method=%d, device=%s, line=%d, gpio=%d, invert=%d\n",
+		ch,
+		ptt_method[ch], 
+		ptt_device[ch],
+		ptt_line[ch],
+		ptt_gpio[ch],
+		ptt_invert[ch]);
+#endif
+	}
+
+/*
+ * Set up serial ports.
+ */
+
+	for (ch=0; ch<ptt_num_channels; ch++) {
+
+	  if (ptt_method[ch] == PTT_METHOD_SERIAL) {
+
+#if __WIN32__
+#else
+	    /* Translate Windows device name into Linux name. */
+	    /* COM1 -> /dev/ttyS0, etc. */
+
+	    if (strncasecmp(ptt_device[ch], "COM", 3) == 0) {
+	      int n = atoi (ptt_device[ch] + 3);
+	      text_color_set(DW_COLOR_INFO);
+	      dw_printf ("Converted PTT device '%s'", ptt_device[ch]);
+	      if (n < 1) n = 1;
+	      sprintf (ptt_device[ch], "/dev/ttyS%d", n-1);
+	      dw_printf (" to Linux equivalent '%s'\n", ptt_device[ch]);
+	    }
+#endif
+	    /* Can't open the same device more than once so we */
+	    /* need more logic to look for the case of both radio */
+	    /* channels using different pins of the same COM port. */
+
+	    /* TODO: Needs to be rewritten in a more general manner */
+	    /* if we ever have more than 2 channels. */
+
+	    if (ch == 1 && strcmp(ptt_device[0],ptt_device[1]) == 0) {
+	      fd = ptt_fd[0];
+	    }
+	    else {
+#if __WIN32__
+
+	      fd = CreateFile(ptt_device[ch],
+			GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+#else
+
+		/* O_NONBLOCK added in version 0.9. */
+		/* https://bugs.launchpad.net/ubuntu/+source/linux/+bug/661321/comments/12 */
+
+	      fd = open (ptt_device[ch], O_RDONLY | O_NONBLOCK);
+#endif
+            }
+
+	    if (fd != INVALID_HANDLE_VALUE) {
+	      ptt_fd[ch] = fd;
+	    }
+	    else {
+#if __WIN32__
+#else
+	      int e = errno;
+#endif
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("ERROR can't open device %s for channel %d PTT control.\n",
+			ptt_device[ch], ch);
+#if __WIN32__
+#else
+	      dw_printf ("%s\n", strerror(errno));
+#endif
+	      /* Don't try using it later if device open failed. */
+
+	      ptt_method[ch] = PTT_METHOD_NONE;
+	    }
+
+/*
+ * Set initial state of PTT off.
+ * ptt_set will invert output signal if appropriate.
+ */	  
+	    ptt_set (ch, 0);
+
+	  } 	/* if serial method. */
+
+	}	/* For each channel. */
+
+
+/* 
+ * Set up GPIO - for Linux only.
+ */
+
+#if __WIN32__
+#else
+
+/*
+ * Does any channel use GPIO?
+ */
+
+	using_gpio = 0;
+	for (ch=0; ch<ptt_num_channels; ch++) {
+	  if (ptt_method[ch] == PTT_METHOD_GPIO) {
+	    using_gpio = 1;
+	  }
+	}
+
+	if (using_gpio) {
+	
+	  struct stat finfo;
+/*
+ * Normally the device nodes are set up for access 
+ * only by root.  Try to change it here so we don't
+ * burden user with another configuration step.
+ *
+ * Does /sys/class/gpio/export even exist?
+ */
+
+	  if (stat("/sys/class/gpio/export", &finfo) < 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("This system is not configured with the GPIO user interface.\n");
+	    dw_printf ("Use a different method for PTT control.\n");
+	    exit (1);
+	  }
+
+/*
+ * Do we have permission to access it?
+ *
+ *	pi at raspberrypi /sys/class/gpio $ ls -l
+ *	total 0
+ *	--w------- 1 root root 4096 Aug 20 07:59 export
+ *	lrwxrwxrwx 1 root root    0 Aug 20 07:59 gpiochip0 -> ../../devices/virtual/gpio/gpiochip0
+ *	--w------- 1 root root 4096 Aug 20 07:59 unexport
+ */
+	  if (geteuid() != 0) {
+	    if ( ! (finfo.st_mode & S_IWOTH)) {
+
+	      /* Try to change protection. */
+	      system ("sudo chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport");
+	      
+	      if (stat("/sys/class/gpio/export", &finfo) < 0) {
+	        /* Unexpected because we could do it before. */
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("This system is not configured with the GPIO user interface.\n");
+	        dw_printf ("Use a different method for PTT control.\n");
+	        exit (1);
+	      }
+
+	      /* Did we succeed in changing the protection? */
+	      if ( ! (finfo.st_mode & S_IWOTH)) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
+	        dw_printf ("Log in as root and type this command:\n");
+	        dw_printf ("    chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport\n");
+	        exit (1);
+	      }	   
+	    }
+	  }
+	}
+/*
+ * We should now be able to create the device nodes for 
+ * the pins we want to use.
+ */
+	    
+	for (ch=0; ch<ptt_num_channels; ch++) {
+	  if (ptt_method[ch] == PTT_METHOD_GPIO) {
+	    char stemp[80];
+	    struct stat finfo;
+
+	    fd = open("/sys/class/gpio/export", O_WRONLY);
+	    if (fd < 0) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
+	      dw_printf ("Log in as root and type this command:\n");
+	      dw_printf ("    chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport\n");
+	      exit (1);
+	    }
+	    sprintf (stemp, "%d", ptt_gpio[ch]);
+	    if (write (fd, stemp, strlen(stemp)) != strlen(stemp)) {
+	      int e = errno;
+	      /* Ignore EBUSY error which seems to mean */
+	      /* the device node already exists. */
+	      if (e != EBUSY) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Error writing \"%s\" to /sys/class/gpio/export, errno=%d\n", stemp, e);
+	        dw_printf ("%s\n", strerror(e));
+	        exit (1);
+	      }
+	    }
+	    close (fd);
+
+/*
+ * We will have the same permission problem if not root.
+ * We only care about "direction" and "value".
+ */
+	    sprintf (stemp, "sudo chmod go+rw /sys/class/gpio/gpio%d/direction", ptt_gpio[ch]);
+	    system (stemp);
+	    sprintf (stemp, "sudo chmod go+rw /sys/class/gpio/gpio%d/value", ptt_gpio[ch]);
+	    system (stemp);
+
+	    sprintf (stemp, "/sys/class/gpio/gpio%d/value", ptt_gpio[ch]);
+
+	    if (stat(stemp, &finfo) < 0) {
+	      int e = errno;
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Failed to get status for %s \n", stemp);
+	      dw_printf ("%s\n", strerror(e));
+	      exit (1);
+	    }
+
+	    if (geteuid() != 0) {
+	      if ( ! (finfo.st_mode & S_IWOTH)) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
+	        dw_printf ("Log in as root and type these commands:\n");
+	        dw_printf ("    chmod go+rw /sys/class/gpio/gpio%d/direction", ptt_gpio[ch]);
+	        dw_printf ("    chmod go+rw /sys/class/gpio/gpio%d/value", ptt_gpio[ch]);
+	        exit (1);
+	      }
+	    }
+
+/*
+ * Set output direction with initial state off.
+ */
+
+	    sprintf (stemp, "/sys/class/gpio/gpio%d/direction", ptt_gpio[ch]);
+	    fd = open(stemp, O_WRONLY);
+	    if (fd < 0) {
+	      int e = errno;
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Error opening %s\n", stemp);
+	      dw_printf ("%s\n", strerror(e));
+	      exit (1);
+	    }
+
+	    char hilo[8];
+	    if (ptt_invert[ch]) {
+	      strcpy (hilo, "high");
+	    }
+	    else {
+	      strcpy (hilo, "low");
+	    }
+	    if (write (fd, hilo, strlen(hilo)) != strlen(hilo)) {
+	      int e = errno;
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Error writing initial state to %s\n", stemp);
+	      dw_printf ("%s\n", strerror(e));
+	      exit (1);
+	    }
+	    close (fd);
+	  }
+	}
+#endif
+
+
+/* Why doesn't it transmit?  Probably forgot to specify PTT option. */
+
+	for (ch=0; ch<ptt_num_channels; ch++) {
+	  if(ptt_method[ch] == PTT_METHOD_NONE) {
+	      text_color_set(DW_COLOR_INFO);
+	      dw_printf ("Note: PTT not configured for channel %d.\n", ch);
+	  }
+	}
+} /* end ptt_init */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        ptt_set
+ *
+ * Purpose:    	Turn transmitter on or off.
+ *
+ * Inputs:	chan	channel, 0 .. (number of channels)-1
+ *
+ *		ptt	1 for transmit, 0 for receive.
+ *
+ *
+ * Assumption:	ptt_init was called first.
+ *
+ * Description:	Set the RTS or DTR line or GPIO pin.
+ *		More positive output means transmit unless invert is set.
+ *
+ *--------------------------------------------------------------------*/
+
+
+void ptt_set (int chan, int ptt)
+{
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("ptt_set ( %d, %d )\n", chan, ptt);
+#endif
+
+/* 
+ * Inverted output? 
+ */
+
+	if (ptt_invert[chan]) {
+	  ptt = ! ptt;
+	}
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+
+/*
+ * Using serial port?
+ */
+	if (ptt_method[chan] == PTT_METHOD_SERIAL && 
+		ptt_fd[chan] != INVALID_HANDLE_VALUE) {
+
+	  if (ptt_line[chan] == PTT_LINE_RTS) {
+
+	    if (ptt) {
+	      RTS_ON(ptt_fd[chan]);
+	    }
+	    else {
+	      RTS_OFF(ptt_fd[chan]);
+	    }
+	  }
+	  else if (ptt_line[chan] == PTT_LINE_DTR) {
+
+	    if (ptt) {
+	      DTR_ON(ptt_fd[chan]);
+	    }
+	    else {
+	      DTR_OFF(ptt_fd[chan]);
+	    }
+	  }
+	}
+
+/*
+ * Using GPIO? 
+ */
+
+#if __WIN32__
+#else
+
+	if (ptt_method[chan] == PTT_METHOD_GPIO) {
+	  int fd;
+	  char stemp[80];
+
+	  sprintf (stemp, "/sys/class/gpio/gpio%d/value", ptt_gpio[chan]);
+
+	  fd = open(stemp, O_WRONLY);
+	  if (fd < 0) {
+	    int e = errno;
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Error opening %s to set PTT signal.\n", stemp);
+	    dw_printf ("%s\n", strerror(e));
+	    return;
+	  }
+
+	  sprintf (stemp, "%d", ptt);
+
+	  if (write (fd, stemp, 1) != 1) {
+	    int e = errno;
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Error setting GPIO %d for PTT\n", ptt_gpio[chan]);
+	    dw_printf ("%s\n", strerror(e));
+	  }
+	  close (fd);
+
+	}
+#endif
+	
+
+} /* end ptt_set */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        ptt_term
+ *
+ * Purpose:    	Make sure PTT is turned off when we exit.
+ *
+ * Inputs:	none
+ *
+ * Description:	
+ *
+ *--------------------------------------------------------------------*/
+
+void ptt_term (void)
+{
+	int n;
+
+	for (n = 0; n < ptt_num_channels; n++) {
+	  ptt_set (n, 0);
+	  if (ptt_fd[n] != INVALID_HANDLE_VALUE) {
+#if __WIN32__
+	    CloseHandle (ptt_fd[n]);
+#else
+	    close(ptt_fd[n]);
+#endif
+	    ptt_fd[n] = INVALID_HANDLE_VALUE;
+	  }
+	}
+}
+
+
+
+
+/*
+ * Quick stand-alone test for above.
+ *
+ *    gcc -DTEST -o ptest ptt.c ; ./ptest
+ *
+ */
+
+
+#if TEST
+
+void text_color_set (dw_color_t c)  {  }
+
+main ()
+{
+	struct audio_s modem;
+	int n;
+	int chan;
+
+	memset (&modem, 0, sizeof(modem));
+
+	modem.num_channels = 2;
+
+	modem.ptt_method[0] = PTT_METHOD_SERIAL;
+	//strcpy (modem.ptt_device[0], "COM1");
+	strcpy (modem.ptt_device[0], "/dev/ttyUSB0");
+	modem.ptt_line[0] = PTT_LINE_RTS;
+
+	modem.ptt_method[1] = PTT_METHOD_SERIAL;
+	//strcpy (modem.ptt_device[1], "COM1");
+	strcpy (modem.ptt_device[1], "/dev/ttyUSB0");
+	modem.ptt_line[1] = PTT_LINE_DTR;
+
+
+/* initialize - both off */
+
+	ptt_init (&modem);
+
+	SLEEP_SEC(2);
+
+/* flash each a few times. */
+
+	dw_printf ("turn on RTS a few times...\n");
+
+	chan = 0;
+	for (n=0; n<3; n++) {
+	  ptt_set (chan, 1);
+	  SLEEP_SEC(1);
+	  ptt_set (chan, 0);
+	  SLEEP_SEC(1);
+	}
+
+	dw_printf ("turn on DTR a few times...\n");
+
+	chan = 1;
+	for (n=0; n<3; n++) {
+	  ptt_set (chan, 1);
+	  SLEEP_SEC(1);
+	  ptt_set (chan, 0);
+	  SLEEP_SEC(1);
+	}
+
+	ptt_term();
+
+/* Same thing again but invert RTS. */
+
+	modem.ptt_invert[0] = 1;
+
+	ptt_init (&modem);
+
+	SLEEP_SEC(2);
+
+	dw_printf ("INVERTED -  RTS a few times...\n");
+
+	chan = 0;
+	for (n=0; n<3; n++) {
+	  ptt_set (chan, 1);
+	  SLEEP_SEC(1);
+	  ptt_set (chan, 0);
+	  SLEEP_SEC(1);
+	}
+
+	dw_printf ("turn on DTR a few times...\n");
+
+	chan = 1;
+	for (n=0; n<3; n++) {
+	  ptt_set (chan, 1);
+	  SLEEP_SEC(1);
+	  ptt_set (chan, 0);
+	  SLEEP_SEC(1);
+	}
+
+	ptt_term ();
+
+
+/* Test GPIO */
+
+#if __WIN32__
+#else
+
+	memset (&modem, 0, sizeof(modem));
+	modem.num_channels = 1;
+	modem.ptt_method[0] = PTT_METHOD_GPIO;
+	modem.ptt_gpio[0] = 25;
+
+	dw_printf ("Try GPIO %d a few times...\n", modem.ptt_gpio[0]);
+
+	ptt_init (&modem);
+
+	SLEEP_SEC(2);
+	chan = 0;
+	for (n=0; n<3; n++) {
+	  ptt_set (chan, 1);
+	  SLEEP_SEC(1);
+	  ptt_set (chan, 0);
+	  SLEEP_SEC(1);
+	}
+
+	ptt_term ();
+#endif
+
+}
+
+#endif
+
+/* end ptt.c */
+
+
+
diff --git a/ptt.h b/ptt.h
new file mode 100755
index 0000000..7c27a20
--- /dev/null
+++ b/ptt.h
@@ -0,0 +1,23 @@
+
+
+#ifndef PTT_H
+#define PTT_H 1
+
+
+#include "audio.h"	/* for struct audio_s */
+
+
+void ptt_init (struct audio_s *p_modem);
+
+void ptt_set (int chan, int ptt); 
+
+void ptt_term (void);
+
+
+#endif
+
+
+/* end ptt.h */
+
+
+
diff --git a/pttest.c b/pttest.c
new file mode 100755
index 0000000..e894561
--- /dev/null
+++ b/pttest.c
@@ -0,0 +1,287 @@
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      pttest.c
+ *
+ * Purpose:   	Test for pseudo terminal.
+ *		
+ * Input:	
+ *
+ * Outputs:	  
+ *
+ * Description:	The protocol name is an acronym for Keep it Simple Stupid.
+ *		You would expect it to be simple but this caused a lot
+ *		of effort on Linux.  The problem is that writes to a pseudo
+ *		terminal eventually block if nothing at the other end 
+ *		is removing the data.  This causes the application to
+ *		hang and stop receiving after a while.
+ *
+ *		This is an attempt to demonstrate the problem in a small
+ *		test case and, hopefully, find a solution.
+ *	
+ *
+ * Instructions:
+ *		First compile like this:
+ *
+ *
+ *		Run it, noting the name of pseudo terminal, 
+ *		typically /dev/pts/1.
+ *
+ *		In another window, type:
+ *
+ *			cat /dev/pts/1
+ *
+ *		This should run "forever" as long as something is
+ *		reading from the slave side of the pseudo terminal.
+ *
+ *		If nothing is removing the data, this runs for a while
+ *		and then blocks on the write.
+ *		For this particular application we just want to discard
+ *		excess data if no one is listening.
+ *
+ *
+ * Failed experiments:
+ *
+ * 		Notice that ??? always returns 0 for amount of data
+ *		in the queue.  
+ *		Define TEST1 to make the device non-blocking.
+ *		Write fails entirely.
+ *
+ *		Define TEST2 to use a different method.
+ *		Also fails in the same way.
+ *
+ *		
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+
+
+#define __USE_XOPEN2KXSI 1
+#define __USE_XOPEN 1
+//#define __USE_POSIX 1
+#include <stdlib.h>
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+
+#include <assert.h>
+#include <string.h>
+
+//#include "direwolf.h"
+//#include "tq.h"
+//#include "ax25_pad.h"
+//#include "textcolor.h"
+//#include "kiss.h"
+//#include "xmit.h"
+
+
+
+static MYFDTYPE pt_master_fd = -1;	/* File descriptor for my end. */
+
+static MYFDTYPE pt_slave_fd = -1;	/* File descriptor for pseudo terminal */
+						/* for use by application. */
+static int msg_number;
+static int total_bytes;
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        kiss_init
+ *
+ * Purpose:     Set up a pseudo terminal acting as a virtual KISS TNC.
+ *		
+ *
+ * Inputs:	mc->nullmodem	- name of device for our end of nullmodem.
+ *
+ * Outputs:	
+ *
+ * Description:	(1) Create a pseudo terminal for the client to use.
+ *		(2) Start a new thread to listen for commands from client app
+ *		    so the main application doesn't block while we wait.
+ *
+ *
+ *--------------------------------------------------------------------*/
+
+static int kiss_open_pt (void);
+
+
+main (int argc, char *argv)
+{
+
+	pt_master_fd = kiss_open_pt ();
+	printf ("msg  total  qcount\n");
+	
+	msg_number = 0;
+	total_bytes = 0;
+
+#endif
+}
+
+
+/*
+ * Returns fd for master side of pseudo terminal or MYFDERROR for error.
+ */
+
+
+static int kiss_open_pt (void)
+{
+	int fd;
+	char *slave_device;
+	struct termios ts;
+	int e;
+	int flags;
+
+	fd = posix_openpt(O_RDWR|O_NOCTTY);
+
+	if (fd == -1
+	    || grantpt (fd) == -1
+	    || unlockpt (fd) == -1
+	    || (slave_device = ptsname (fd)) == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  printf ("ERROR - Could not create pseudo terminal.\n");
+	  return (-1);
+	}
+
+
+	e = tcgetattr (fd, &ts);
+	if (e != 0) { 
+	  printf ("Can't get pseudo terminal attributes, err=%d\n", e);
+	  perror ("pt tcgetattr"); 
+	}
+
+	cfmakeraw (&ts);
+	
+	ts.c_cc[VMIN] = 1;	/* wait for at least one character */
+	ts.c_cc[VTIME] = 0;	/* no fancy timing. */
+				
+
+	e = tcsetattr (fd, TCSANOW, &ts);
+	if (e != 0) { 
+	  text_color_set(DW_COLOR_ERROR);
+	  printf ("Can't set pseudo terminal attributes, err=%d\n", e);
+	  perror ("pt tcsetattr"); 
+	}
+
+/*
+ * After running for a while on Linux, the write eventually
+ * blocks if no one is reading from the other side of
+ * the pseudo terminal.  We get stuck on the kiss data
+ * write and reception stops.
+ *
+ * I tried using ioctl(,TIOCOUTQ,) to see how much was in 
+ * the queue but that always returned zero.  (Ubuntu)
+ *
+ * Let's try using non-blocking writes and see if we get
+ * the EWOULDBLOCK status instead of hanging.
+ */
+
+#if TEST1 	
+	// this is worse. all writes fail. errno = ? bad file descriptor
+	flags = fcntl(fd, F_GETFL, 0);
+	e = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
+	if (e != 0) { 
+	  printf ("Can't set pseudo terminal to nonblocking, fcntl returns %d, errno = %d\n", e, errno);
+	  perror ("pt fcntl"); 
+	}
+#endif
+#if TEST2  
+	// same  
+	flags = 1;	
+	e = ioctl (fd, FIONBIO, &flags);
+	if (e != 0) { 
+	  printf ("Can't set pseudo terminal to nonblocking, ioctl returns %d, errno = %d\n", e, errno);
+	  perror ("pt ioctl"); 
+	}
+#endif
+
+	printf("Virtual KISS TNC is available on %s\n", slave_device);
+	
+
+	// Sample code shows this. Why would we open it here?
+
+	// pt_slave_fd = open(slave_device, O_RDWR|O_NOCTTY);
+
+
+	return (fd);
+}
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        kiss_send_rec_packet
+ *
+ * Purpose:     Send a received packet to the client app.
+ *
+ * Inputs:	chan		- Channel number where packet was received.
+ *				  0 = first, 1 = second if any.
+ *
+ *		pp		- Identifier for packet object.
+ *
+ *		fbuf		- Address of raw received frame buffer.
+ *		flen		- Length of raw received frame.
+ *				  Not including the FCS.
+ *		
+ *
+ * Description:	Send message to client.
+ *		We really don't care if anyone is listening or not.
+ *		I don't even know if we can find out.
+ *
+ *
+ *--------------------------------------------------------------------*/
+
+
+void kiss_send_rec_packet (void)
+{
+
+
+	char kiss_buff[100];
+
+	int kiss_len;
+	int q_count = 123;
+
+
+	int j;
+
+	strcpy (kiss_buff, "The quick brown fox jumps over the lazy dog.\n");
+	kiss_len = strlen(kiss_buff);
+
+
+	if (pt_master_fd != MYFDERROR) {
+	  int err;
+
+	  msg_number++;
+	  total_bytes += kiss_len;
+
+//#if DEBUG
+	  printf ("%3d  %5d  %5d\n", msg_number, total_bytes, q_count);
+//#endif
+          err = write (pt_master_fd, kiss_buff, kiss_len);
+
+	  if (err == -1 && errno == EWOULDBLOCK) {
+//#if DEBUG 
+	    printf ("Discarding message because write would block.\n");
+//#endif
+	  }
+	  else if (err != kiss_len)
+	  {
+	    printf ("\nError sending message on pseudo terminal.  len=%d, write returned %d, errno = %d\n\n",
+		kiss_len, err, errno);
+	    perror ("pt write"); 
+	  }
+
+	}
+//#endif
+
+
+	
+}
+
+
+/* end pttest.c */
diff --git a/rdq.c b/rdq.c
new file mode 100755
index 0000000..57f18f8
--- /dev/null
+++ b/rdq.c
@@ -0,0 +1,453 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      rdq.c
+ *
+ * Purpose:   	Retry later decode queue for frames with bad FCS.
+ *		
+ * Description:	
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "audio.h"
+#include "rdq.h"
+#include "dedupe.h"
+
+
+
+static rrbb_t queue_head;			/* Head of linked list for queue. */
+
+#if __WIN32__
+
+static CRITICAL_SECTION rdq_cs;			/* Critical section for updating queues. */
+
+static HANDLE wake_up_event;			/* Notify try decode again thread when queue not empty. */
+
+#else
+
+static pthread_mutex_t rdq_mutex;		/* Critical section for updating queues. */
+
+static pthread_cond_t wake_up_cond;		/* Notify try decode again thread when queue not empty. */
+
+static pthread_mutex_t wake_up_mutex;		/* Required by cond_wait. */
+
+#endif
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        rdq_init
+ *
+ * Purpose:     Initialize the receive decode again queue.
+ *
+ * Inputs:	None.  Only single queue for all channels.
+ *
+ * Outputs:	
+ *
+ * Description:	Initialize the queue to be empty and set up other
+ *		mechanisms for sharing it between different threads.
+ *
+ *--------------------------------------------------------------------*/
+
+
+void rdq_init (void)
+{
+	//int c, p;
+#if __WIN32__
+#else
+	int err;
+#endif
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("rdq_init (  )\n");
+	dw_printf ("rdq_init: pthread_mutex_init...\n");
+#endif
+
+#if __WIN32__
+	InitializeCriticalSection (&rdq_cs);
+#else
+	err = pthread_mutex_init (&wake_up_mutex, NULL);
+	err = pthread_mutex_init (&rdq_mutex, NULL);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_init: pthread_mutex_init err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("rdq_init: pthread_cond_init...\n");
+#endif
+
+#if __WIN32__
+
+	wake_up_event = CreateEvent (NULL, 0, 0, NULL);
+
+	if (wake_up_event == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_init: pthread_cond_init: can't create decode wake up event");
+	  exit (1);
+	}
+
+#else
+	err = pthread_cond_init (&wake_up_cond, NULL);
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("rdq_init: pthread_cond_init returns %d\n", err);
+#endif
+
+
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_init: pthread_cond_init err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+
+} /* end rdq_init */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        rdq_append
+ *
+ * Purpose:     Add a packet to the end of the queue.
+ *
+ * Inputs:	pp	- Address of raw received bit buffer.
+ *				Caller should NOT make any references to
+ *				it after this point because it could
+ *				be deleted at any time.
+ *
+ * Outputs:	
+ *
+ * Description:	Add buffer to end of linked list.
+ *		Signal the decode thread if the queue was formerly empty.
+ *
+ *--------------------------------------------------------------------*/
+
+void rdq_append (rrbb_t rrbb)
+{
+	//int was_empty;
+	rrbb_t plast;
+	rrbb_t pnext;
+#ifndef __WIN32__
+	int err;
+#endif
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("rdq_append (rrbb=%p)\n", rrbb);
+	dw_printf ("rdq_append: enter critical section\n");
+#endif
+#if __WIN32__
+	EnterCriticalSection (&rdq_cs);
+#else
+	err = pthread_mutex_lock (&rdq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_append: pthread_mutex_lock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+	//was_empty = 1;
+	//if (queue_head != NULL) {
+	       //was_empty = 0;
+	//}
+	if (queue_head == NULL) {
+	  queue_head = rrbb;
+	}
+	else {
+	  plast = queue_head;
+	  while ((pnext = rrbb_get_nextp(plast)) != NULL) {
+	    plast = pnext;
+	  }
+	  rrbb_set_nextp (plast, rrbb);
+	}
+
+
+#if __WIN32__ 
+	LeaveCriticalSection (&rdq_cs);
+#else
+	err = pthread_mutex_unlock (&rdq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_append: pthread_mutex_unlock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("rdq_append: left critical section\n");
+	dw_printf ("rdq_append (): about to wake up retry decode thread.\n");
+#endif
+
+#if __WIN32__
+	SetEvent (wake_up_event);
+#else
+	err = pthread_mutex_lock (&wake_up_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_append: pthread_mutex_lock wu err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+
+	err = pthread_cond_signal (&wake_up_cond);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_append: pthread_cond_signal err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+
+	err = pthread_mutex_unlock (&wake_up_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_append: pthread_mutex_unlock wu err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        rdq_wait_while_empty
+ *
+ * Purpose:     Sleep while the queue is empty rather than
+ *		polling periodically.
+ *
+ * Inputs:	None.
+ *		
+ *--------------------------------------------------------------------*/
+
+
+void rdq_wait_while_empty (void)
+{
+	int is_empty;
+#ifndef __WIN32__
+	int err;
+#endif
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("rdq_wait_while_empty () : enter critical section\n");
+#endif
+
+#if __WIN32__
+	EnterCriticalSection (&rdq_cs);
+#else
+	err = pthread_mutex_lock (&rdq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_wait_while_empty: pthread_mutex_lock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+#if DEBUG
+	//text_color_set(DW_COLOR_DEBUG);
+	//dw_printf ("rdq_wait_while_empty (): after pthread_mutex_lock\n");
+#endif
+	is_empty = 1;
+        if (queue_head != NULL)
+	       is_empty = 0;
+
+
+#if __WIN32__
+	LeaveCriticalSection (&rdq_cs);
+#else
+	err = pthread_mutex_unlock (&rdq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_wait_while_empty: pthread_mutex_unlock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("rdq_wait_while_empty () : left critical section\n");
+#endif
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("rdq_wait_while_empty (): is_empty = %d\n", is_empty);
+#endif
+
+	if (is_empty) {
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("rdq_wait_while_empty (): SLEEP - about to call cond wait\n");
+#endif
+
+
+#if __WIN32__
+	  WaitForSingleObject (wake_up_event, INFINITE);
+
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("rdq_wait_while_empty (): returned from wait\n");
+#endif
+
+#else
+	  err = pthread_mutex_lock (&wake_up_mutex);
+	  if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("rdq_wait_while_empty: pthread_mutex_lock wu err=%d", err);
+	    perror ("");
+	    exit (1);
+	  }
+
+	  err = pthread_cond_wait (&wake_up_cond, &wake_up_mutex);
+
+
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("rdq_wait_while_empty (): WOKE UP - returned from cond wait, err = %d\n", err);
+#endif
+
+	  if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("rdq_wait_while_empty: pthread_cond_wait err=%d", err);
+	    perror ("");
+	    exit (1);
+	  }
+
+	  err = pthread_mutex_unlock (&wake_up_mutex);
+	  if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("rdq_wait_while_empty: pthread_mutex_unlock wu err=%d", err);
+	    perror ("");
+	    exit (1);
+	  }
+
+#endif
+	}
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("rdq_wait_while_empty () returns\n");
+#endif
+
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        rdq_remove
+ *
+ * Purpose:     Remove raw bit buffer from the head of the queue.
+ *
+ * Inputs:	none
+ *
+ * Returns:	Pointer to rrbb object.
+ *		Caller should destroy it with rrbb_delete when finished with it.	
+ *
+ *--------------------------------------------------------------------*/
+
+rrbb_t rdq_remove (void)
+{
+
+	rrbb_t result_p;
+#ifndef __WIN32__
+	int err;
+#endif
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("rdq_remove() enter critical section\n");
+#endif
+
+#if __WIN32__
+	EnterCriticalSection (&rdq_cs);
+#else
+	err = pthread_mutex_lock (&rdq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_remove: pthread_mutex_lock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+	if (queue_head == NULL) {
+	  result_p = NULL;
+	}
+	else {
+
+	  result_p = queue_head;
+	  queue_head = rrbb_get_nextp(result_p);
+	  rrbb_set_nextp (result_p, NULL);
+	}
+	 
+#if __WIN32__
+	LeaveCriticalSection (&rdq_cs);
+#else
+	err = pthread_mutex_unlock (&rdq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("rdq_remove: pthread_mutex_unlock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("rdq_remove() leave critical section, returns %p\n", result_p);
+#endif
+	return (result_p);
+}
+
+
+
+/* end rdq.c */
diff --git a/rdq.h b/rdq.h
new file mode 100755
index 0000000..dcb2896
--- /dev/null
+++ b/rdq.h
@@ -0,0 +1,28 @@
+
+/*------------------------------------------------------------------
+ *
+ * Module:      rdq.h
+ *
+ * Purpose:   	Retry decode queue - Hold raw received frames with errors
+ *		for retrying the decoding later.
+ *		
+ *---------------------------------------------------------------*/
+
+#ifndef RDQ_H
+#define RDQ_H 1
+
+#include "rrbb.h"
+//#include "audio.h"
+
+void rdq_init (void);
+
+void rdq_append (rrbb_t rrbb);
+
+void rdq_wait_while_empty (void);
+
+rrbb_t rdq_remove (void);
+
+
+#endif
+
+/* end rdq.h */
diff --git a/redecode.c b/redecode.c
new file mode 100755
index 0000000..0a90702
--- /dev/null
+++ b/redecode.c
@@ -0,0 +1,252 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      redecode.c
+ *
+ * Purpose:   	Retry decoding frames that have a bad FCS.
+ *		
+ * Description:	
+ *
+ *
+ * Usage:	(1) The main application calls redecode_init.
+ *
+ *			This will initialize the retry decoding queue
+ *			and create a thread to work on contents of the queue.
+ *
+ *		(2) The application queues up frames by calling rdq_append.
+ *
+ *
+ *		(3) redecode_thread removes raw frames from the queue and 
+ *			tries to recover from errors.
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+//#include <sys/time.h>
+#include <time.h>
+
+#if __WIN32__
+#include <windows.h>
+#endif
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "audio.h"
+#include "rdq.h"
+#include "redecode.h"
+#include "hdlc_send.h"
+#include "hdlc_rec2.h"
+#include "ptt.h"
+
+
+
+
+
+
+#if __WIN32__
+static unsigned redecode_thread (void *arg);
+#else
+static void * redecode_thread (void *arg);
+#endif
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        redecode_init
+ *
+ * Purpose:     Initialize the process to try fixing bits in frames with bad FCS.
+ *
+ * Inputs:	none.
+ *
+ * Outputs:	none.
+ *
+ * Description:	Initialize the queue to be empty and set up other
+ *		mechanisms for sharing it between different threads.
+ *
+ *		Start up redecode_thread to actually process the
+ *		raw frames from the queue.
+ *
+ *--------------------------------------------------------------------*/
+
+
+
+void redecode_init (void)
+{
+	//int j;
+#if __WIN32__
+	HANDLE redecode_th;
+#else
+	pthread_t redecode_tid;
+	int e;
+#endif
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("redecode_init ( ... )\n");
+#endif
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("redecode_init: about to call rdq_init \n");
+#endif
+	rdq_init ();
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("redecode_init: about to create thread \n");
+#endif
+
+
+#if __WIN32__
+	redecode_th = _beginthreadex (NULL, 0, redecode_thread, NULL, 0, NULL);
+	if (redecode_th == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not create redecode thread\n");
+	  return;
+	}
+#else
+
+//TODO: Give thread lower priority.
+
+	e = pthread_create (&redecode_tid, NULL, redecode_thread, (void *)0);
+	if (e != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror("Could not create redecode thread");
+	  return;
+	}
+#endif
+
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("redecode_init: finished \n");
+#endif
+
+
+} /* end redecode_init */
+
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        redecode_thread
+ *
+ * Purpose:     Try to decode frames with a bad FCS.
+ *
+ * Inputs:	None.
+ *
+ * Outputs:	
+ *
+ * Description:	Initialize the queue to be empty and set up other
+ *		mechanisms for sharing it between different threads.
+ *
+ *
+ *--------------------------------------------------------------------*/
+
+#if __WIN32__
+static unsigned redecode_thread (void *arg)
+#else
+static void * redecode_thread (void *arg)
+#endif
+{
+	rrbb_t block;
+	int blen;
+	int chan, subchan;
+	int alevel;
+
+
+#if __WIN32__
+	HANDLE tid = GetCurrentThread();
+	//int tp;
+
+	//tp = GetThreadPriority (tid);
+	//text_color_set(DW_COLOR_DEBUG);
+	//dw_printf ("Starting redecode thread priority=%d\n", tp);
+	SetThreadPriority (tid, THREAD_PRIORITY_LOWEST);
+	//tp = GetThreadPriority (tid);
+	//dw_printf ("New redecode thread priority=%d\n", tp);
+#endif
+
+	while (1) {
+
+	  rdq_wait_while_empty ();
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("redecode_thread: woke up\n");
+#endif
+	  
+	  block = rdq_remove ();
+
+
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("redecode_thread: rdq_remove() returned %p\n", block);
+#endif
+
+/* Don't expect null ever but be safe. */
+
+	  if (block != NULL) {
+
+	    chan = rrbb_get_chan(block);
+	    subchan = rrbb_get_subchan(block);
+	    alevel = rrbb_get_audio_level(block);
+	    blen = rrbb_get_len(block);
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("redecode_thread: begin processing %p, from channel %d, blen=%d\n", block, chan, blen);
+#endif
+
+	    hdlc_rec2_try_to_fix_later (block, chan, subchan, alevel);
+
+#if DEBUG
+	    text_color_set(DW_COLOR_DEBUG);
+	    dw_printf ("redecode_thread: finished processing %p\n", block);
+#endif
+	    rrbb_delete (block);
+	  }
+
+	}
+
+	return 0;
+
+} /* end redecode_thread */
+
+
+
+
+
+/* end redecode.c */
+
+
+
diff --git a/redecode.h b/redecode.h
new file mode 100755
index 0000000..87995b7
--- /dev/null
+++ b/redecode.h
@@ -0,0 +1,15 @@
+
+
+#ifndef REDECODE_H
+#define REDECODE_H 1
+
+#include "rrbb.h"	
+
+
+extern void redecode_init (void);
+
+
+#endif
+
+/* end redecode.h */
+
diff --git a/regex/COPYING b/regex/COPYING
new file mode 100755
index 0000000..5b6e7c6
--- /dev/null
+++ b/regex/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  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.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+

+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+

+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+

+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+

+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+		     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
+convey 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 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/regex/COPYING.LIB b/regex/COPYING.LIB
new file mode 100755
index 0000000..cf9b6b9
--- /dev/null
+++ b/regex/COPYING.LIB
@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+^L
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+^L
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+

+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+^L
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+^L
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+^L
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+^L
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+^L
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+^L
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey 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 library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser 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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James
+  Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/regex/INSTALL b/regex/INSTALL
new file mode 100755
index 0000000..8d61b3e
--- /dev/null
+++ b/regex/INSTALL
@@ -0,0 +1,463 @@
+Installing the GNU C Library
+****************************
+
+Before you do anything else, you should read the file `FAQ' located at
+the top level of the source tree.  This file answers common questions
+and describes problems you may experience with compilation and
+installation.  It is updated more frequently than this manual.
+
+   Features can be added to GNU Libc via "add-on" bundles.  These are
+separate tar files, which you unpack into the top level of the source
+tree.  Then you give `configure' the `--enable-add-ons' option to
+activate them, and they will be compiled into the library.
+
+   You will need recent versions of several GNU tools: definitely GCC
+and GNU Make, and possibly others.  *Note Tools for Compilation::,
+below.
+
+Configuring and compiling GNU Libc
+==================================
+
+GNU libc cannot be compiled in the source directory.  You must build it
+in a separate build directory.  For example, if you have unpacked the
+glibc sources in `/src/gnu/glibc-2.4', create a directory
+`/src/gnu/glibc-build' to put the object files in.  This allows
+removing the whole build directory in case an error occurs, which is
+the safest way to get a fresh start and should always be done.
+
+   From your object directory, run the shell script `configure' located
+at the top level of the source tree.  In the scenario above, you'd type
+
+     $ ../glibc-2.4/configure ARGS...
+
+   Please note that even though you're building in a separate build
+directory, the compilation needs to modify a few files in the source
+directory, especially some files in the manual subdirectory.
+
+`configure' takes many options, but the only one that is usually
+mandatory is `--prefix'.  This option tells `configure' where you want
+glibc installed.  This defaults to `/usr/local', but the normal setting
+to install as the standard system library is `--prefix=/usr' for
+GNU/Linux systems and `--prefix=' (an empty prefix) for GNU/Hurd
+systems.
+
+   It may also be useful to set the CC and CFLAGS variables in the
+environment when running `configure'.  CC selects the C compiler that
+will be used, and CFLAGS sets optimization options for the compiler.
+
+   The following list describes all of the available options for
+`configure':
+
+`--prefix=DIRECTORY'
+     Install machine-independent data files in subdirectories of
+     `DIRECTORY'.  The default is to install in `/usr/local'.
+
+`--exec-prefix=DIRECTORY'
+     Install the library and other machine-dependent files in
+     subdirectories of `DIRECTORY'.  The default is to the `--prefix'
+     directory if that option is specified, or `/usr/local' otherwise.
+
+`--with-headers=DIRECTORY'
+     Look for kernel header files in DIRECTORY, not `/usr/include'.
+     Glibc needs information from the kernel's private header files.
+     Glibc will normally look in `/usr/include' for them, but if you
+     specify this option, it will look in DIRECTORY instead.
+
+     This option is primarily of use on a system where the headers in
+     `/usr/include' come from an older version of glibc.  Conflicts can
+     occasionally happen in this case.  Note that Linux libc5 qualifies
+     as an older version of glibc.  You can also use this option if you
+     want to compile glibc with a newer set of kernel headers than the
+     ones found in `/usr/include'.
+
+`--enable-add-ons[=LIST]'
+     Specify add-on packages to include in the build.  If this option is
+     specified with no list, it enables all the add-on packages it
+     finds in the main source directory; this is the default behavior.
+     You may specify an explicit list of add-ons to use in LIST,
+     separated by spaces or commas (if you use spaces, remember to
+     quote them from the shell).  Each add-on in LIST can be an
+     absolute directory name or can be a directory name relative to the
+     main source directory, or relative to the build directory (that
+     is, the current working directory).  For example,
+     `--enable-add-ons=nptl,../glibc-libidn-2.4'.
+
+`--enable-kernel=VERSION'
+     This option is currently only useful on GNU/Linux systems.  The
+     VERSION parameter should have the form X.Y.Z and describes the
+     smallest version of the Linux kernel the generated library is
+     expected to support.  The higher the VERSION number is, the less
+     compatibility code is added, and the faster the code gets.
+
+`--with-binutils=DIRECTORY'
+     Use the binutils (assembler and linker) in `DIRECTORY', not the
+     ones the C compiler would default to.  You can use this option if
+     the default binutils on your system cannot deal with all the
+     constructs in the GNU C library.  In that case, `configure' will
+     detect the problem and suppress these constructs, so that the
+     library will still be usable, but functionality may be lost--for
+     example, you can't build a shared libc with old binutils.
+
+`--without-fp'
+     Use this option if your computer lacks hardware floating-point
+     support and your operating system does not emulate an FPU.
+
+     these
+
+`--disable-shared'
+     Don't build shared libraries even if it is possible.  Not all
+     systems support shared libraries; you need ELF support and
+     (currently) the GNU linker.
+
+`--disable-profile'
+     Don't build libraries with profiling information.  You may want to
+     use this option if you don't plan to do profiling.
+
+`--enable-omitfp'
+     Use maximum optimization for the normal (static and shared)
+     libraries, and compile separate static libraries with debugging
+     information and no optimization.  We recommend not doing this.
+     The extra optimization doesn't gain you much, it may provoke
+     compiler bugs, and you won't be able to trace bugs through the C
+     library.
+
+`--disable-versioning'
+     Don't compile the shared libraries with symbol version information.
+     Doing this will make the resulting library incompatible with old
+     binaries, so it's not recommended.
+
+`--enable-static-nss'
+     Compile static versions of the NSS (Name Service Switch) libraries.
+     This is not recommended because it defeats the purpose of NSS; a
+     program linked statically with the NSS libraries cannot be
+     dynamically reconfigured to use a different name database.
+
+`--without-tls'
+     By default the C library is built with support for thread-local
+     storage if the used tools support it.  By using `--without-tls'
+     this can be prevented though there generally is no reason since it
+     creates compatibility problems.
+
+`--build=BUILD-SYSTEM'
+`--host=HOST-SYSTEM'
+     These options are for cross-compiling.  If you specify both
+     options and BUILD-SYSTEM is different from HOST-SYSTEM, `configure'
+     will prepare to cross-compile glibc from BUILD-SYSTEM to be used
+     on HOST-SYSTEM.  You'll probably need the `--with-headers' option
+     too, and you may have to override CONFIGURE's selection of the
+     compiler and/or binutils.
+
+     If you only specify `--host', `configure' will prepare for a
+     native compile but use what you specify instead of guessing what
+     your system is. This is most useful to change the CPU submodel.
+     For example, if `configure' guesses your machine as
+     `i586-pc-linux-gnu' but you want to compile a library for 386es,
+     give `--host=i386-pc-linux-gnu' or just `--host=i386-linux' and add
+     the appropriate compiler flags (`-mcpu=i386' will do the trick) to
+     CFLAGS.
+
+     If you specify just `--build', `configure' will get confused.
+
+   To build the library and related programs, type `make'.  This will
+produce a lot of output, some of which may look like errors from `make'
+but isn't.  Look for error messages from `make' containing `***'.
+Those indicate that something is seriously wrong.
+
+   The compilation process can take a long time, depending on the
+configuration and the speed of your machine.  Some complex modules may
+take a very long time to compile, as much as several minutes on slower
+machines.  Do not panic if the compiler appears to hang.
+
+   If you want to run a parallel make, simply pass the `-j' option with
+an appropriate numeric parameter to `make'.  You need a recent GNU
+`make' version, though.
+
+   To build and run test programs which exercise some of the library
+facilities, type `make check'.  If it does not complete successfully,
+do not use the built library, and report a bug after verifying that the
+problem is not already known.  *Note Reporting Bugs::, for instructions
+on reporting bugs.  Note that some of the tests assume they are not
+being run by `root'.  We recommend you compile and test glibc as an
+unprivileged user.
+
+   Before reporting bugs make sure there is no problem with your system.
+The tests (and later installation) use some pre-existing files of the
+system such as `/etc/passwd', `/etc/nsswitch.conf' and others.  These
+files must all contain correct and sensible content.
+
+   To format the `GNU C Library Reference Manual' for printing, type
+`make dvi'.  You need a working TeX installation to do this.  The
+distribution already includes the on-line formatted version of the
+manual, as Info files.  You can regenerate those with `make info', but
+it shouldn't be necessary.
+
+   The library has a number of special-purpose configuration parameters
+which you can find in `Makeconfig'.  These can be overwritten with the
+file `configparms'.  To change them, create a `configparms' in your
+build directory and add values as appropriate for your system.  The
+file is included and parsed by `make' and has to follow the conventions
+for makefiles.
+
+   It is easy to configure the GNU C library for cross-compilation by
+setting a few variables in `configparms'.  Set `CC' to the
+cross-compiler for the target you configured the library for; it is
+important to use this same `CC' value when running `configure', like
+this: `CC=TARGET-gcc configure TARGET'.  Set `BUILD_CC' to the compiler
+to use for programs run on the build system as part of compiling the
+library.  You may need to set `AR' and `RANLIB' to cross-compiling
+versions of `ar' and `ranlib' if the native tools are not configured to
+work with object files for the target you configured for.
+
+Installing the C Library
+========================
+
+To install the library and its header files, and the Info files of the
+manual, type `env LANGUAGE=C LC_ALL=C make install'.  This will build
+things, if necessary, before installing them; however, you should still
+compile everything first.  If you are installing glibc as your primary
+C library, we recommend that you shut the system down to single-user
+mode first, and reboot afterward.  This minimizes the risk of breaking
+things when the library changes out from underneath.
+
+   If you're upgrading from Linux libc5 or some other C library, you
+need to replace the `/usr/include' with a fresh directory before
+installing it.  The new `/usr/include' should contain the Linux
+headers, but nothing else.
+
+   You must first build the library (`make'), optionally check it
+(`make check'), switch the include directories and then install (`make
+install').  The steps must be done in this order.  Not moving the
+directory before install will result in an unusable mixture of header
+files from both libraries, but configuring, building, and checking the
+library requires the ability to compile and run programs against the old
+library.
+
+   If you are upgrading from a previous installation of glibc 2.0 or
+2.1, `make install' will do the entire job.  You do not need to remove
+the old includes - if you want to do so anyway you must then follow the
+order given above.
+
+   You may also need to reconfigure GCC to work with the new library.
+The easiest way to do that is to figure out the compiler switches to
+make it work again (`-Wl,--dynamic-linker=/lib/ld-linux.so.2' should
+work on GNU/Linux systems) and use them to recompile gcc.  You can also
+edit the specs file (`/usr/lib/gcc-lib/TARGET/VERSION/specs'), but that
+is a bit of a black art.
+
+   You can install glibc somewhere other than where you configured it
+to go by setting the `install_root' variable on the command line for
+`make install'.  The value of this variable is prepended to all the
+paths for installation.  This is useful when setting up a chroot
+environment or preparing a binary distribution.  The directory should be
+specified with an absolute file name.
+
+   Glibc 2.2 includes a daemon called `nscd', which you may or may not
+want to run.  `nscd' caches name service lookups; it can dramatically
+improve performance with NIS+, and may help with DNS as well.
+
+   One auxiliary program, `/usr/libexec/pt_chown', is installed setuid
+`root'.  This program is invoked by the `grantpt' function; it sets the
+permissions on a pseudoterminal so it can be used by the calling
+process.  This means programs like `xterm' and `screen' do not have to
+be setuid to get a pty.  (There may be other reasons why they need
+privileges.)  If you are using a 2.1 or newer Linux kernel with the
+`devptsfs' or `devfs' filesystems providing pty slaves, you don't need
+this program; otherwise you do.  The source for `pt_chown' is in
+`login/programs/pt_chown.c'.
+
+   After installation you might want to configure the timezone and
+locale installation of your system.  The GNU C library comes with a
+locale database which gets configured with `localedef'.  For example, to
+set up a German locale with name `de_DE', simply issue the command
+`localedef -i de_DE -f ISO-8859-1 de_DE'.  To configure all locales
+that are supported by glibc, you can issue from your build directory the
+command `make localedata/install-locales'.
+
+   To configure the locally used timezone, set the `TZ' environment
+variable.  The script `tzselect' helps you to select the right value.
+As an example, for Germany, `tzselect' would tell you to use
+`TZ='Europe/Berlin''.  For a system wide installation (the given paths
+are for an installation with `--prefix=/usr'), link the timezone file
+which is in `/usr/share/zoneinfo' to the file `/etc/localtime'.  For
+Germany, you might execute `ln -s /usr/share/zoneinfo/Europe/Berlin
+/etc/localtime'.
+
+Recommended Tools for Compilation
+=================================
+
+We recommend installing the following GNU tools before attempting to
+build the GNU C library:
+
+   * GNU `make' 3.79 or newer
+
+     You need the latest version of GNU `make'.  Modifying the GNU C
+     Library to work with other `make' programs would be so difficult
+     that we recommend you port GNU `make' instead.  *Really.*  We
+     recommend GNU `make' version 3.79.  All earlier versions have
+     severe bugs or lack features.
+
+   * GCC 3.4 or newer, GCC 4.1 recommended
+
+     The GNU C library can only be compiled with the GNU C compiler
+     family.  For the 2.3 releases, GCC 3.2 or higher is required; GCC
+     3.4 is the compiler we advise to use for 2.3 versions.  For the
+     2.4 release, GCC 3.4 or higher is required; as of this writing,
+     GCC 4.1 is the compiler we advise to use for current versions.  On
+     certain machines including `powerpc64', compilers prior to GCC 4.0
+     have bugs that prevent them compiling the C library code in the
+     2.4 release.  On other machines, GCC 4.1 is required to build the C
+     library with support for the correct `long double' type format;
+     these include `powerpc' (32 bit), `s390' and `s390x'.
+
+     You can use whatever compiler you like to compile programs that
+     use GNU libc, but be aware that both GCC 2.7 and 2.8 have bugs in
+     their floating-point support that may be triggered by the math
+     library.
+
+     Check the FAQ for any special compiler issues on particular
+     platforms.
+
+   * GNU `binutils' 2.15 or later
+
+     You must use GNU `binutils' (as and ld) to build the GNU C library.
+     No other assembler or linker has the necessary functionality at the
+     moment.
+
+   * GNU `texinfo' 3.12f
+
+     To correctly translate and install the Texinfo documentation you
+     need this version of the `texinfo' package.  Earlier versions do
+     not understand all the tags used in the document, and the
+     installation mechanism for the info files is not present or works
+     differently.
+
+   * GNU `awk' 3.0, or higher
+
+     `Awk' is used in several places to generate files.  `gawk' 3.0 is
+     known to work.
+
+   * Perl 5
+
+     Perl is not required, but it is used if present to test the
+     installation.  We may decide to use it elsewhere in the future.
+
+   * GNU `sed' 3.02 or newer
+
+     `Sed' is used in several places to generate files.  Most scripts
+     work with any version of `sed'.  The known exception is the script
+     `po2test.sed' in the `intl' subdirectory which is used to generate
+     `msgs.h' for the test suite.  This script works correctly only
+     with GNU `sed' 3.02.  If you like to run the test suite, you
+     should definitely upgrade `sed'.
+
+
+If you change any of the `configure.in' files you will also need
+
+   * GNU `autoconf' 2.53 or higher
+
+and if you change any of the message translation files you will need
+
+   * GNU `gettext' 0.10.36 or later
+
+You may also need these packages if you upgrade your source tree using
+patches, although we try to avoid this.
+
+Specific advice for GNU/Linux systems
+=====================================
+
+If you are installing GNU libc on a GNU/Linux system, you need to have
+the header files from a 2.2 or newer kernel around for reference.  For
+some architectures, like ia64, sh and hppa, you need at least headers
+from kernel 2.3.99 (sh and hppa) or 2.4.0 (ia64).  You do not need to
+use that kernel, just have its headers where glibc can access at them.
+The easiest way to do this is to unpack it in a directory such as
+`/usr/src/linux-2.2.1'.  In that directory, run `make config' and
+accept all the defaults.  Then run `make include/linux/version.h'.
+Finally, configure glibc with the option
+`--with-headers=/usr/src/linux-2.2.1/include'.  Use the most recent
+kernel you can get your hands on.
+
+   An alternate tactic is to unpack the 2.2 kernel and run `make
+config' as above; then, rename or delete `/usr/include', create a new
+`/usr/include', and make symbolic links of `/usr/include/linux' and
+`/usr/include/asm' into the kernel sources.  You can then configure
+glibc with no special options.  This tactic is recommended if you are
+upgrading from libc5, since you need to get rid of the old header files
+anyway.
+
+   After installing GNU libc, you may need to remove or rename
+`/usr/include/linux' and `/usr/include/asm', and replace them with
+copies of `include/linux' and `include/asm-$ARCHITECTURE' taken from
+the Linux source package which supplied kernel headers for building the
+library.  ARCHITECTURE will be the machine architecture for which the
+library was built, such as `i386' or `alpha'.  You do not need to do
+this if you did not specify an alternate kernel header source using
+`--with-headers'.  The intent here is that these directories should be
+copies of, *not* symlinks to, the kernel headers used to build the
+library.
+
+   Note that `/usr/include/net' and `/usr/include/scsi' should *not* be
+symlinks into the kernel sources.  GNU libc provides its own versions
+of these files.
+
+   GNU/Linux expects some components of the libc installation to be in
+`/lib' and some in `/usr/lib'.  This is handled automatically if you
+configure glibc with `--prefix=/usr'.  If you set some other prefix or
+allow it to default to `/usr/local', then all the components are
+installed there.
+
+   If you are upgrading from libc5, you need to recompile every shared
+library on your system against the new library for the sake of new code,
+but keep the old libraries around for old binaries to use.  This is
+complicated and difficult.  Consult the Glibc2 HOWTO at
+`http://www.imaxx.net/~thrytis/glibc' for details.
+
+   You cannot use `nscd' with 2.0 kernels, due to bugs in the
+kernel-side thread support.  `nscd' happens to hit these bugs
+particularly hard, but you might have problems with any threaded
+program.
+
+Reporting Bugs
+==============
+
+There are probably bugs in the GNU C library.  There are certainly
+errors and omissions in this manual.  If you report them, they will get
+fixed.  If you don't, no one will ever know about them and they will
+remain unfixed for all eternity, if not longer.
+
+   It is a good idea to verify that the problem has not already been
+reported.  Bugs are documented in two places: The file `BUGS' describes
+a number of well known bugs and the bug tracking system has a WWW
+interface at `http://sources.redhat.com/bugzilla/'.  The WWW interface
+gives you access to open and closed reports.  A closed report normally
+includes a patch or a hint on solving the problem.
+
+   To report a bug, first you must find it.  With any luck, this will
+be the hard part.  Once you've found a bug, make sure it's really a
+bug.  A good way to do this is to see if the GNU C library behaves the
+same way some other C library does.  If so, probably you are wrong and
+the libraries are right (but not necessarily).  If not, one of the
+libraries is probably wrong.  It might not be the GNU library.  Many
+historical Unix C libraries permit things that we don't, such as
+closing a file twice.
+
+   If you think you have found some way in which the GNU C library does
+not conform to the ISO and POSIX standards (*note Standards and
+Portability::), that is definitely a bug.  Report it!
+
+   Once you're sure you've found a bug, try to narrow it down to the
+smallest test case that reproduces the problem.  In the case of a C
+library, you really only need to narrow it down to one library function
+call, if possible.  This should not be too difficult.
+
+   The final step when you have a simple test case is to report the bug.
+Do this using the WWW interface to the bug database.
+
+   If you are not sure how a function should behave, and this manual
+doesn't tell you, that's a bug in the manual.  Report that too!  If the
+function's behavior disagrees with the manual, then either the library
+or the manual has a bug, so report the disagreement.  If you find any
+errors or omissions in this manual, please report them to the bug
+database.  If you refer to specific sections of the manual, please
+include the section names for easier identification.
+
diff --git a/regex/LICENSES b/regex/LICENSES
new file mode 100755
index 0000000..b3b8899
--- /dev/null
+++ b/regex/LICENSES
@@ -0,0 +1,219 @@
+This file contains the copying permission notices for various files in the
+GNU C Library distribution that have copyright owners other than the Free
+Software Foundation.  These notices all require that a copy of the notice
+be included in the accompanying documentation and be distributed with
+binary distributions of the code, so be sure to include this file along
+with any binary distributions derived from the GNU C Library.
+
+

+All code incorporated from 4.4 BSD is distributed under the following
+license:
+
+Copyright (C) 1991 Regents of the University of California.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. [This condition was removed.]
+4. Neither the name of the University nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+

+The DNS resolver code, taken from BIND 4.9.5, is copyrighted both by
+UC Berkeley and by Digital Equipment Corporation.  The DEC portions
+are under the following license:
+
+Portions Copyright (C) 1993 by Digital Equipment Corporation.
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies, and
+that the name of Digital Equipment Corporation not be used in
+advertising or publicity pertaining to distribution of the document or
+software without specific, written prior permission.
+
+THE SOFTWARE IS PROVIDED ``AS IS'' AND DIGITAL EQUIPMENT CORP.
+DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL
+DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+

+The Sun RPC support (from rpcsrc-4.0) is covered by the following
+license:
+
+Copyright (C) 1984, Sun Microsystems, Inc.
+
+Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+unrestricted use provided that this legend is included on all tape media
+and as a part of the software program in whole or part.  Users may copy
+or modify Sun RPC without charge, but are not authorized to license or
+distribute it to anyone else except as part of a product or program
+developed by the user.
+
+SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+
+Sun RPC is provided with no support and without any obligation on the
+part of Sun Microsystems, Inc. to assist in its use, correction,
+modification or enhancement.
+
+SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+OR ANY PART THEREOF.
+
+In no event will Sun Microsystems, Inc. be liable for any lost revenue
+or profits or other special, indirect and consequential damages, even if
+Sun has been advised of the possibility of such damages.
+
+

+The following CMU license covers some of the support code for Mach,
+derived from Mach 3.0:
+
+Mach Operating System
+Copyright (C) 1991,1990,1989 Carnegie Mellon University
+All Rights Reserved.
+
+Permission to use, copy, modify and distribute this software and its
+documentation is hereby granted, provided that both the copyright
+notice and this permission notice appear in all copies of the
+software, derivative works or modified versions, and any portions
+thereof, and that both notices appear in supporting documentation.
+
+CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS ``AS IS''
+CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+
+Carnegie Mellon requests users of this software to return to
+
+ Software Distribution Coordinator
+ School of Computer Science
+ Carnegie Mellon University
+ Pittsburgh PA 15213-3890
+
+or Software.Distribution at CS.CMU.EDU any improvements or
+extensions that they make and grant Carnegie Mellon the rights to
+redistribute these changes.
+

+The file if_ppp.h is under the following CMU license:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY AND
+ CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+

+The following license covers the files from Intel's "Highly Optimized
+Mathematical Functions for Itanium" collection:
+
+Intel License Agreement
+
+Copyright (c) 2000, Intel Corporation
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+* The name of Intel Corporation may not be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+

+The files inet/getnameinfo.c and sysdeps/posix/getaddrinfo.c are copyright
+(C) by Craig Metz and are distributed under the following license:
+
+/* The Inner Net License, Version 2.00
+
+  The author(s) grant permission for redistribution and use in source and
+binary forms, with or without modification, of the software and documentation
+provided that the following conditions are met:
+
+0. If you receive a version of the software that is specifically labelled
+   as not being for redistribution (check the version message and/or README),
+   you are not permitted to redistribute that version of the software in any
+   way or form.
+1. All terms of the all other applicable copyrights and licenses must be
+   followed.
+2. Redistributions of source code must retain the authors' copyright
+   notice(s), this list of conditions, and the following disclaimer.
+3. Redistributions in binary form must reproduce the authors' copyright
+   notice(s), this list of conditions, and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+4. [The copyright holder has authorized the removal of this clause.]
+5. Neither the name(s) of the author(s) nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  If these license terms cause you a real problem, contact the author.  */
diff --git a/regex/NEWS b/regex/NEWS
new file mode 100755
index 0000000..e69de29
diff --git a/regex/README b/regex/README
new file mode 100755
index 0000000..e69de29
diff --git a/regex/README-dire-wolf.txt b/regex/README-dire-wolf.txt
new file mode 100755
index 0000000..58ca0b9
--- /dev/null
+++ b/regex/README-dire-wolf.txt
@@ -0,0 +1,6 @@
+For Linux and Cygwin, we use the built-in regular expression library.
+For the Windows version, we need to include our own version.
+
+The source was obtained from:
+
+	http://gnuwin32.sourceforge.net/packages/regex.htm
\ No newline at end of file
diff --git a/regex/re_comp.h b/regex/re_comp.h
new file mode 100755
index 0000000..4911447
--- /dev/null
+++ b/regex/re_comp.h
@@ -0,0 +1,26 @@
+/*  Copyright (C) 1996 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _RE_COMP_H
+#define _RE_COMP_H	1
+
+/* This is only a wrapper around the <regex.h> file.  XPG4.2 mentions
+   this name.  */
+#include <regex.h>
+
+#endif /* re_comp.h */
diff --git a/regex/regcomp.c b/regex/regcomp.c
new file mode 100755
index 0000000..4cf1688
--- /dev/null
+++ b/regex/regcomp.c
@@ -0,0 +1,3801 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002,2003,2004,2005,2006,2007 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu at yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
+					  size_t length, reg_syntax_t syntax);
+static void re_compile_fastmap_iter (regex_t *bufp,
+				     const re_dfastate_t *init_state,
+				     char *fastmap);
+static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len);
+#ifdef RE_ENABLE_I18N
+static void free_charset (re_charset_t *cset);
+#endif /* RE_ENABLE_I18N */
+static void free_workarea_compile (regex_t *preg);
+static reg_errcode_t create_initial_state (re_dfa_t *dfa);
+#ifdef RE_ENABLE_I18N
+static void optimize_utf8 (re_dfa_t *dfa);
+#endif
+static reg_errcode_t analyze (regex_t *preg);
+static reg_errcode_t preorder (bin_tree_t *root,
+			       reg_errcode_t (fn (void *, bin_tree_t *)),
+			       void *extra);
+static reg_errcode_t postorder (bin_tree_t *root,
+				reg_errcode_t (fn (void *, bin_tree_t *)),
+				void *extra);
+static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node);
+static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node);
+static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg,
+				 bin_tree_t *node);
+static reg_errcode_t calc_first (void *extra, bin_tree_t *node);
+static reg_errcode_t calc_next (void *extra, bin_tree_t *node);
+static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node);
+static int duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint);
+static int search_duplicated_node (const re_dfa_t *dfa, int org_node,
+				   unsigned int constraint);
+static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
+static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
+					 int node, int root);
+static reg_errcode_t calc_inveclosure (re_dfa_t *dfa);
+static int fetch_number (re_string_t *input, re_token_t *token,
+			 reg_syntax_t syntax);
+static int peek_token (re_token_t *token, re_string_t *input,
+			reg_syntax_t syntax) internal_function;
+static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
+			  reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg,
+				  re_token_t *token, reg_syntax_t syntax,
+				  int nest, reg_errcode_t *err);
+static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg,
+				 re_token_t *token, reg_syntax_t syntax,
+				 int nest, reg_errcode_t *err);
+static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg,
+				     re_token_t *token, reg_syntax_t syntax,
+				     int nest, reg_errcode_t *err);
+static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg,
+				  re_token_t *token, reg_syntax_t syntax,
+				  int nest, reg_errcode_t *err);
+static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp,
+				 re_dfa_t *dfa, re_token_t *token,
+				 reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa,
+				      re_token_t *token, reg_syntax_t syntax,
+				      reg_errcode_t *err);
+static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
+					    re_string_t *regexp,
+					    re_token_t *token, int token_len,
+					    re_dfa_t *dfa,
+					    reg_syntax_t syntax,
+					    int accept_hyphen);
+static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
+					  re_string_t *regexp,
+					  re_token_t *token);
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
+					re_charset_t *mbcset,
+					int *equiv_class_alloc,
+					const unsigned char *name);
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+				      bitset_t sbcset,
+				      re_charset_t *mbcset,
+				      int *char_class_alloc,
+				      const unsigned char *class_name,
+				      reg_syntax_t syntax);
+#else  /* not RE_ENABLE_I18N */
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
+					const unsigned char *name);
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+				      bitset_t sbcset,
+				      const unsigned char *class_name,
+				      reg_syntax_t syntax);
+#endif /* not RE_ENABLE_I18N */
+static bin_tree_t *build_charclass_op (re_dfa_t *dfa,
+				       RE_TRANSLATE_TYPE trans,
+				       const unsigned char *class_name,
+				       const unsigned char *extra,
+				       int non_match, reg_errcode_t *err);
+static bin_tree_t *create_tree (re_dfa_t *dfa,
+				bin_tree_t *left, bin_tree_t *right,
+				re_token_type_t type);
+static bin_tree_t *create_token_tree (re_dfa_t *dfa,
+				      bin_tree_t *left, bin_tree_t *right,
+				      const re_token_t *token);
+static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
+static void free_token (re_token_t *node);
+static reg_errcode_t free_tree (void *extra, bin_tree_t *node);
+static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node);
+

+/* This table gives an error message for each of the error codes listed
+   in regex.h.  Obviously the order here has to be same as there.
+   POSIX doesn't require that we do anything for REG_NOERROR,
+   but why not be nice?  */
+
+const char __re_error_msgid[] attribute_hidden =
+  {
+#define REG_NOERROR_IDX	0
+    gettext_noop ("Success")	/* REG_NOERROR */
+    "\0"
+#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
+    gettext_noop ("No match")	/* REG_NOMATCH */
+    "\0"
+#define REG_BADPAT_IDX	(REG_NOMATCH_IDX + sizeof "No match")
+    gettext_noop ("Invalid regular expression") /* REG_BADPAT */
+    "\0"
+#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
+    gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
+    "\0"
+#define REG_ECTYPE_IDX	(REG_ECOLLATE_IDX + sizeof "Invalid collation character")
+    gettext_noop ("Invalid character class name") /* REG_ECTYPE */
+    "\0"
+#define REG_EESCAPE_IDX	(REG_ECTYPE_IDX + sizeof "Invalid character class name")
+    gettext_noop ("Trailing backslash") /* REG_EESCAPE */
+    "\0"
+#define REG_ESUBREG_IDX	(REG_EESCAPE_IDX + sizeof "Trailing backslash")
+    gettext_noop ("Invalid back reference") /* REG_ESUBREG */
+    "\0"
+#define REG_EBRACK_IDX	(REG_ESUBREG_IDX + sizeof "Invalid back reference")
+    gettext_noop ("Unmatched [ or [^")	/* REG_EBRACK */
+    "\0"
+#define REG_EPAREN_IDX	(REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+    gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
+    "\0"
+#define REG_EBRACE_IDX	(REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
+    gettext_noop ("Unmatched \\{") /* REG_EBRACE */
+    "\0"
+#define REG_BADBR_IDX	(REG_EBRACE_IDX + sizeof "Unmatched \\{")
+    gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
+    "\0"
+#define REG_ERANGE_IDX	(REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
+    gettext_noop ("Invalid range end")	/* REG_ERANGE */
+    "\0"
+#define REG_ESPACE_IDX	(REG_ERANGE_IDX + sizeof "Invalid range end")
+    gettext_noop ("Memory exhausted") /* REG_ESPACE */
+    "\0"
+#define REG_BADRPT_IDX	(REG_ESPACE_IDX + sizeof "Memory exhausted")
+    gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
+    "\0"
+#define REG_EEND_IDX	(REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
+    gettext_noop ("Premature end of regular expression") /* REG_EEND */
+    "\0"
+#define REG_ESIZE_IDX	(REG_EEND_IDX + sizeof "Premature end of regular expression")
+    gettext_noop ("Regular expression too big") /* REG_ESIZE */
+    "\0"
+#define REG_ERPAREN_IDX	(REG_ESIZE_IDX + sizeof "Regular expression too big")
+    gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
+  };
+
+const size_t __re_error_msgid_idx[] attribute_hidden =
+  {
+    REG_NOERROR_IDX,
+    REG_NOMATCH_IDX,
+    REG_BADPAT_IDX,
+    REG_ECOLLATE_IDX,
+    REG_ECTYPE_IDX,
+    REG_EESCAPE_IDX,
+    REG_ESUBREG_IDX,
+    REG_EBRACK_IDX,
+    REG_EPAREN_IDX,
+    REG_EBRACE_IDX,
+    REG_BADBR_IDX,
+    REG_ERANGE_IDX,
+    REG_ESPACE_IDX,
+    REG_BADRPT_IDX,
+    REG_EEND_IDX,
+    REG_ESIZE_IDX,
+    REG_ERPAREN_IDX
+  };
+

+/* Entry points for GNU code.  */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+   compiles PATTERN (of length LENGTH) and puts the result in BUFP.
+   Returns 0 if the pattern was valid, otherwise an error string.
+
+   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+   are set in BUFP on entry.  */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+    const char *pattern;
+    size_t length;
+    struct re_pattern_buffer *bufp;
+{
+  reg_errcode_t ret;
+
+  /* And GNU code determines whether or not to get register information
+     by passing null for the REGS argument to re_match, etc., not by
+     setting no_sub, unless RE_NO_SUB is set.  */
+  bufp->no_sub = !!(re_syntax_options & RE_NO_SUB);
+
+  /* Match anchors at newline.  */
+  bufp->newline_anchor = 1;
+
+  ret = re_compile_internal (bufp, pattern, length, re_syntax_options);
+
+  if (!ret)
+    return NULL;
+  return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+#ifdef _LIBC
+weak_alias (__re_compile_pattern, re_compile_pattern)
+#endif
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
+   also be assigned to arbitrarily: each pattern buffer stores its own
+   syntax, so it can be changed between regex compilations.  */
+/* This has no initializer because initialized variables in Emacs
+   become read-only after dumping.  */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation.  This provides
+   for compatibility for various utilities which historically have
+   different, incompatible syntaxes.
+
+   The argument SYNTAX is a bit mask comprised of the various bits
+   defined in regex.h.  We return the old syntax.  */
+
+reg_syntax_t
+re_set_syntax (syntax)
+    reg_syntax_t syntax;
+{
+  reg_syntax_t ret = re_syntax_options;
+
+  re_syntax_options = syntax;
+  return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_set_syntax, re_set_syntax)
+#endif
+
+int
+re_compile_fastmap (bufp)
+    struct re_pattern_buffer *bufp;
+{
+  re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+  char *fastmap = bufp->fastmap;
+
+  memset (fastmap, '\0', sizeof (char) * SBC_MAX);
+  re_compile_fastmap_iter (bufp, dfa->init_state, fastmap);
+  if (dfa->init_state != dfa->init_state_word)
+    re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap);
+  if (dfa->init_state != dfa->init_state_nl)
+    re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap);
+  if (dfa->init_state != dfa->init_state_begbuf)
+    re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap);
+  bufp->fastmap_accurate = 1;
+  return 0;
+}
+#ifdef _LIBC
+weak_alias (__re_compile_fastmap, re_compile_fastmap)
+#endif
+
+static inline void
+__attribute ((always_inline))
+re_set_fastmap (char *fastmap, int icase, int ch)
+{
+  fastmap[ch] = 1;
+  if (icase)
+    fastmap[tolower (ch)] = 1;
+}
+
+/* Helper function for re_compile_fastmap.
+   Compile fastmap for the initial_state INIT_STATE.  */
+
+static void
+re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state,
+			 char *fastmap)
+{
+  re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+  int node_cnt;
+  int icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE));
+  for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt)
+    {
+      int node = init_state->nodes.elems[node_cnt];
+      re_token_type_t type = dfa->nodes[node].type;
+
+      if (type == CHARACTER)
+	{
+	  re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c);
+#ifdef RE_ENABLE_I18N
+	  if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+	    {
+	      unsigned char *buf = alloca (dfa->mb_cur_max), *p;
+	      wchar_t wc;
+	      mbstate_t state;
+
+	      p = buf;
+	      *p++ = dfa->nodes[node].opr.c;
+	      while (++node < dfa->nodes_len
+		     &&	dfa->nodes[node].type == CHARACTER
+		     && dfa->nodes[node].mb_partial)
+		*p++ = dfa->nodes[node].opr.c;
+	      memset (&state, '\0', sizeof (state));
+	      if (mbrtowc (&wc, (const char *) buf, p - buf,
+			   &state) == p - buf
+		  && (__wcrtomb ((char *) buf, towlower (wc), &state)
+		      != (size_t) -1))
+		re_set_fastmap (fastmap, 0, buf[0]);
+	    }
+#endif
+	}
+      else if (type == SIMPLE_BRACKET)
+	{
+	  int i, ch;
+	  for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+	    {
+	      int j;
+	      bitset_word_t w = dfa->nodes[node].opr.sbcset[i];
+	      for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+		if (w & ((bitset_word_t) 1 << j))
+		  re_set_fastmap (fastmap, icase, ch);
+	    }
+	}
+#ifdef RE_ENABLE_I18N
+      else if (type == COMPLEX_BRACKET)
+	{
+	  int i;
+	  re_charset_t *cset = dfa->nodes[node].opr.mbcset;
+	  if (cset->non_match || cset->ncoll_syms || cset->nequiv_classes
+	      || cset->nranges || cset->nchar_classes)
+	    {
+# ifdef _LIBC
+	      if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0)
+		{
+		  /* In this case we want to catch the bytes which are
+		     the first byte of any collation elements.
+		     e.g. In da_DK, we want to catch 'a' since "aa"
+			  is a valid collation element, and don't catch
+			  'b' since 'b' is the only collation element
+			  which starts from 'b'.  */
+		  const int32_t *table = (const int32_t *)
+		    _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+		  for (i = 0; i < SBC_MAX; ++i)
+		    if (table[i] < 0)
+		      re_set_fastmap (fastmap, icase, i);
+		}
+# else
+	      if (dfa->mb_cur_max > 1)
+		for (i = 0; i < SBC_MAX; ++i)
+		  if (__btowc (i) == WEOF)
+		    re_set_fastmap (fastmap, icase, i);
+# endif /* not _LIBC */
+	    }
+	  for (i = 0; i < cset->nmbchars; ++i)
+	    {
+	      char buf[256];
+	      mbstate_t state;
+	      memset (&state, '\0', sizeof (state));
+	      if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1)
+		re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
+	      if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+		{
+		  if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state)
+		      != (size_t) -1)
+		    re_set_fastmap (fastmap, 0, *(unsigned char *) buf);
+		}
+	    }
+	}
+#endif /* RE_ENABLE_I18N */
+      else if (type == OP_PERIOD
+#ifdef RE_ENABLE_I18N
+	       || type == OP_UTF8_PERIOD
+#endif /* RE_ENABLE_I18N */
+	       || type == END_OF_RE)
+	{
+	  memset (fastmap, '\1', sizeof (char) * SBC_MAX);
+	  if (type == END_OF_RE)
+	    bufp->can_be_null = 1;
+	  return;
+	}
+    }
+}
+

+/* Entry point for POSIX code.  */
+/* regcomp takes a regular expression as a string and compiles it.
+
+   PREG is a regex_t *.  We do not expect any fields to be initialized,
+   since POSIX says we shouldn't.  Thus, we set
+
+     `buffer' to the compiled pattern;
+     `used' to the length of the compiled pattern;
+     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+       REG_EXTENDED bit in CFLAGS is set; otherwise, to
+       RE_SYNTAX_POSIX_BASIC;
+     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+     `fastmap' to an allocated space for the fastmap;
+     `fastmap_accurate' to zero;
+     `re_nsub' to the number of subexpressions in PATTERN.
+
+   PATTERN is the address of the pattern string.
+
+   CFLAGS is a series of bits which affect compilation.
+
+     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+     use POSIX basic syntax.
+
+     If REG_NEWLINE is set, then . and [^...] don't match newline.
+     Also, regexec will try a match beginning after every newline.
+
+     If REG_ICASE is set, then we considers upper- and lowercase
+     versions of letters to be equivalent when matching.
+
+     If REG_NOSUB is set, then when PREG is passed to regexec, that
+     routine will report only success or failure, and nothing about the
+     registers.
+
+   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
+   the return codes and their meanings.)  */
+
+int
+regcomp (preg, pattern, cflags)
+    regex_t *__restrict preg;
+    const char *__restrict pattern;
+    int cflags;
+{
+  reg_errcode_t ret;
+  reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED
+			 : RE_SYNTAX_POSIX_BASIC);
+
+  preg->buffer = NULL;
+  preg->allocated = 0;
+  preg->used = 0;
+
+  /* Try to allocate space for the fastmap.  */
+  preg->fastmap = re_malloc (char, SBC_MAX);
+  if (BE (preg->fastmap == NULL, 0))
+    return REG_ESPACE;
+
+  syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0;
+
+  /* If REG_NEWLINE is set, newlines are treated differently.  */
+  if (cflags & REG_NEWLINE)
+    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
+      syntax &= ~RE_DOT_NEWLINE;
+      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+      /* It also changes the matching behavior.  */
+      preg->newline_anchor = 1;
+    }
+  else
+    preg->newline_anchor = 0;
+  preg->no_sub = !!(cflags & REG_NOSUB);
+  preg->translate = NULL;
+
+  ret = re_compile_internal (preg, pattern, strlen (pattern), syntax);
+
+  /* POSIX doesn't distinguish between an unmatched open-group and an
+     unmatched close-group: both are REG_EPAREN.  */
+  if (ret == REG_ERPAREN)
+    ret = REG_EPAREN;
+
+  /* We have already checked preg->fastmap != NULL.  */
+  if (BE (ret == REG_NOERROR, 1))
+    /* Compute the fastmap now, since regexec cannot modify the pattern
+       buffer.  This function never fails in this implementation.  */
+    (void) re_compile_fastmap (preg);
+  else
+    {
+      /* Some error occurred while compiling the expression.  */
+      re_free (preg->fastmap);
+      preg->fastmap = NULL;
+    }
+
+  return (int) ret;
+}
+#ifdef _LIBC
+weak_alias (__regcomp, regcomp)
+#endif
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+   from either regcomp or regexec.   We don't use PREG here.  */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+    int errcode;
+    const regex_t *__restrict preg;
+    char *__restrict errbuf;
+    size_t errbuf_size;
+{
+  const char *msg;
+  size_t msg_size;
+
+  if (BE (errcode < 0
+	  || errcode >= (int) (sizeof (__re_error_msgid_idx)
+			       / sizeof (__re_error_msgid_idx[0])), 0))
+    /* Only error codes returned by the rest of the code should be passed
+       to this routine.  If we are given anything else, or if other regex
+       code generates an invalid error code, then the program has a bug.
+       Dump core so we can fix it.  */
+    abort ();
+
+  msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]);
+
+  msg_size = strlen (msg) + 1; /* Includes the null.  */
+
+  if (BE (errbuf_size != 0, 1))
+    {
+      if (BE (msg_size > errbuf_size, 0))
+	{
+#if defined HAVE_MEMPCPY || defined _LIBC
+	  *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0';
+#else
+	  memcpy (errbuf, msg, errbuf_size - 1);
+	  errbuf[errbuf_size - 1] = 0;
+#endif
+	}
+      else
+	memcpy (errbuf, msg, msg_size);
+    }
+
+  return msg_size;
+}
+#ifdef _LIBC
+weak_alias (__regerror, regerror)
+#endif
+
+
+#ifdef RE_ENABLE_I18N
+/* This static array is used for the map to single-byte characters when
+   UTF-8 is used.  Otherwise we would allocate memory just to initialize
+   it the same all the time.  UTF-8 is the preferred encoding so this is
+   a worthwhile optimization.  */
+static const bitset_t utf8_sb_map =
+{
+  /* Set the first 128 bits.  */
+  [0 ... 0x80 / BITSET_WORD_BITS - 1] = BITSET_WORD_MAX
+};
+#endif
+
+
+static void
+free_dfa_content (re_dfa_t *dfa)
+{
+  int i, j;
+
+  if (dfa->nodes)
+    for (i = 0; i < dfa->nodes_len; ++i)
+      free_token (dfa->nodes + i);
+  re_free (dfa->nexts);
+  for (i = 0; i < dfa->nodes_len; ++i)
+    {
+      if (dfa->eclosures != NULL)
+	re_node_set_free (dfa->eclosures + i);
+      if (dfa->inveclosures != NULL)
+	re_node_set_free (dfa->inveclosures + i);
+      if (dfa->edests != NULL)
+	re_node_set_free (dfa->edests + i);
+    }
+  re_free (dfa->edests);
+  re_free (dfa->eclosures);
+  re_free (dfa->inveclosures);
+  re_free (dfa->nodes);
+
+  if (dfa->state_table)
+    for (i = 0; i <= dfa->state_hash_mask; ++i)
+      {
+	struct re_state_table_entry *entry = dfa->state_table + i;
+	for (j = 0; j < entry->num; ++j)
+	  {
+	    re_dfastate_t *state = entry->array[j];
+	    free_state (state);
+	  }
+        re_free (entry->array);
+      }
+  re_free (dfa->state_table);
+#ifdef RE_ENABLE_I18N
+  if (dfa->sb_char != utf8_sb_map)
+    re_free (dfa->sb_char);
+#endif
+  re_free (dfa->subexp_map);
+#ifdef DEBUG
+  re_free (dfa->re_str);
+#endif
+
+  re_free (dfa);
+}
+
+
+/* Free dynamically allocated space used by PREG.  */
+
+void
+regfree (preg)
+    regex_t *preg;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  if (BE (dfa != NULL, 1))
+    free_dfa_content (dfa);
+  preg->buffer = NULL;
+  preg->allocated = 0;
+
+  re_free (preg->fastmap);
+  preg->fastmap = NULL;
+
+  re_free (preg->translate);
+  preg->translate = NULL;
+}
+#ifdef _LIBC
+weak_alias (__regfree, regfree)
+#endif
+

+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them unless specifically requested.  */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+
+/* BSD has one and only one pattern buffer.  */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+# ifdef _LIBC
+/* Make these definitions weak in libc, so POSIX programs can redefine
+   these names if they don't use our functions, and still use
+   regcomp/regexec above without link errors.  */
+weak_function
+# endif
+re_comp (s)
+     const char *s;
+{
+  reg_errcode_t ret;
+  char *fastmap;
+
+  if (!s)
+    {
+      if (!re_comp_buf.buffer)
+	return gettext ("No previous regular expression");
+      return 0;
+    }
+
+  if (re_comp_buf.buffer)
+    {
+      fastmap = re_comp_buf.fastmap;
+      re_comp_buf.fastmap = NULL;
+      __regfree (&re_comp_buf);
+      memset (&re_comp_buf, '\0', sizeof (re_comp_buf));
+      re_comp_buf.fastmap = fastmap;
+    }
+
+  if (re_comp_buf.fastmap == NULL)
+    {
+      re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
+      if (re_comp_buf.fastmap == NULL)
+	return (char *) gettext (__re_error_msgid
+				 + __re_error_msgid_idx[(int) REG_ESPACE]);
+    }
+
+  /* Since `re_exec' always passes NULL for the `regs' argument, we
+     don't need to initialize the pattern buffer fields which affect it.  */
+
+  /* Match anchors at newlines.  */
+  re_comp_buf.newline_anchor = 1;
+
+  ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options);
+
+  if (!ret)
+    return NULL;
+
+  /* Yes, we're discarding `const' here if !HAVE_LIBINTL.  */
+  return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+
+#ifdef _LIBC
+libc_freeres_fn (free_mem)
+{
+  __regfree (&re_comp_buf);
+}
+#endif
+
+#endif /* _REGEX_RE_COMP */
+

+/* Internal entry point.
+   Compile the regular expression PATTERN, whose length is LENGTH.
+   SYNTAX indicate regular expression's syntax.  */
+
+static reg_errcode_t
+re_compile_internal (regex_t *preg, const char * pattern, size_t length,
+		     reg_syntax_t syntax)
+{
+  reg_errcode_t err = REG_NOERROR;
+  re_dfa_t *dfa;
+  re_string_t regexp;
+
+  /* Initialize the pattern buffer.  */
+  preg->fastmap_accurate = 0;
+  preg->syntax = syntax;
+  preg->not_bol = preg->not_eol = 0;
+  preg->used = 0;
+  preg->re_nsub = 0;
+  preg->can_be_null = 0;
+  preg->regs_allocated = REGS_UNALLOCATED;
+
+  /* Initialize the dfa.  */
+  dfa = (re_dfa_t *) preg->buffer;
+  if (BE (preg->allocated < sizeof (re_dfa_t), 0))
+    {
+      /* If zero allocated, but buffer is non-null, try to realloc
+	 enough space.  This loses if buffer's address is bogus, but
+	 that is the user's responsibility.  If ->buffer is NULL this
+	 is a simple allocation.  */
+      dfa = re_realloc (preg->buffer, re_dfa_t, 1);
+      if (dfa == NULL)
+	return REG_ESPACE;
+      preg->allocated = sizeof (re_dfa_t);
+      preg->buffer = (unsigned char *) dfa;
+    }
+  preg->used = sizeof (re_dfa_t);
+
+  err = init_dfa (dfa, length);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_dfa_content (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+      return err;
+    }
+#ifdef DEBUG
+  /* Note: length+1 will not overflow since it is checked in init_dfa.  */
+  dfa->re_str = re_malloc (char, length + 1);
+  strncpy (dfa->re_str, pattern, length + 1);
+#endif
+
+  __libc_lock_init (dfa->lock);
+
+  err = re_string_construct (&regexp, pattern, length, preg->translate,
+			     syntax & RE_ICASE, dfa);
+  if (BE (err != REG_NOERROR, 0))
+    {
+    re_compile_internal_free_return:
+      free_workarea_compile (preg);
+      re_string_destruct (&regexp);
+      free_dfa_content (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+      return err;
+    }
+
+  /* Parse the regular expression, and build a structure tree.  */
+  preg->re_nsub = 0;
+  dfa->str_tree = parse (&regexp, preg, syntax, &err);
+  if (BE (dfa->str_tree == NULL, 0))
+    goto re_compile_internal_free_return;
+
+  /* Analyze the tree and create the nfa.  */
+  err = analyze (preg);
+  if (BE (err != REG_NOERROR, 0))
+    goto re_compile_internal_free_return;
+
+#ifdef RE_ENABLE_I18N
+  /* If possible, do searching in single byte encoding to speed things up.  */
+  if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL)
+    optimize_utf8 (dfa);
+#endif
+
+  /* Then create the initial state of the dfa.  */
+  err = create_initial_state (dfa);
+
+  /* Release work areas.  */
+  free_workarea_compile (preg);
+  re_string_destruct (&regexp);
+
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_dfa_content (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+    }
+
+  return err;
+}
+
+/* Initialize DFA.  We use the length of the regular expression PAT_LEN
+   as the initial length of some arrays.  */
+
+static reg_errcode_t
+init_dfa (re_dfa_t *dfa, size_t pat_len)
+{
+  unsigned int table_size;
+#ifndef _LIBC
+  char *codeset_name;
+#endif
+
+  memset (dfa, '\0', sizeof (re_dfa_t));
+
+  /* Force allocation of str_tree_storage the first time.  */
+  dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+
+  /* Avoid overflows.  */
+  if (pat_len == SIZE_MAX)
+    return REG_ESPACE;
+
+  dfa->nodes_alloc = pat_len + 1;
+  dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
+
+  /*  table_size = 2 ^ ceil(log pat_len) */
+  for (table_size = 1; ; table_size <<= 1)
+    if (table_size > pat_len)
+      break;
+
+  dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
+  dfa->state_hash_mask = table_size - 1;
+
+  dfa->mb_cur_max = MB_CUR_MAX;
+#ifdef _LIBC
+  if (dfa->mb_cur_max == 6
+      && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0)
+    dfa->is_utf8 = 1;
+  dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII)
+		       != 0);
+#else
+# ifdef HAVE_LANGINFO_CODESET
+  codeset_name = nl_langinfo (CODESET);
+# else
+  codeset_name = getenv ("LC_ALL");
+  if (codeset_name == NULL || codeset_name[0] == '\0')
+    codeset_name = getenv ("LC_CTYPE");
+  if (codeset_name == NULL || codeset_name[0] == '\0')
+    codeset_name = getenv ("LANG");
+  if (codeset_name == NULL)
+    codeset_name = "";
+  else if (strchr (codeset_name, '.') !=  NULL)
+    codeset_name = strchr (codeset_name, '.') + 1;
+# endif
+
+  if (strcasecmp (codeset_name, "UTF-8") == 0
+      || strcasecmp (codeset_name, "UTF8") == 0)
+    dfa->is_utf8 = 1;
+
+  /* We check exhaustively in the loop below if this charset is a
+     superset of ASCII.  */
+  dfa->map_notascii = 0;
+#endif
+
+#ifdef RE_ENABLE_I18N
+  if (dfa->mb_cur_max > 1)
+    {
+      if (dfa->is_utf8)
+	dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map;
+      else
+	{
+	  int i, j, ch;
+
+	  dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+	  if (BE (dfa->sb_char == NULL, 0))
+	    return REG_ESPACE;
+
+	  /* Set the bits corresponding to single byte chars.  */
+	  for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+	    for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+	      {
+		wint_t wch = __btowc (ch);
+		if (wch != WEOF)
+		  dfa->sb_char[i] |= (bitset_word_t) 1 << j;
+# ifndef _LIBC
+		if (isascii (ch) && wch != ch)
+		  dfa->map_notascii = 1;
+# endif
+	      }
+	}
+    }
+#endif
+
+  if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0))
+    return REG_ESPACE;
+  return REG_NOERROR;
+}
+
+/* Initialize WORD_CHAR table, which indicate which character is
+   "word".  In this case "word" means that it is the word construction
+   character used by some operators like "\<", "\>", etc.  */
+
+static void
+internal_function
+init_word_char (re_dfa_t *dfa)
+{
+  int i, j, ch;
+  dfa->word_ops_used = 1;
+  for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+    for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+      if (isalnum (ch) || ch == '_')
+	dfa->word_char[i] |= (bitset_word_t) 1 << j;
+}
+
+/* Free the work area which are only used while compiling.  */
+
+static void
+free_workarea_compile (regex_t *preg)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_storage_t *storage, *next;
+  for (storage = dfa->str_tree_storage; storage; storage = next)
+    {
+      next = storage->next;
+      re_free (storage);
+    }
+  dfa->str_tree_storage = NULL;
+  dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+  dfa->str_tree = NULL;
+  re_free (dfa->org_indices);
+  dfa->org_indices = NULL;
+}
+
+/* Create initial states for all contexts.  */
+
+static reg_errcode_t
+create_initial_state (re_dfa_t *dfa)
+{
+  int first, i;
+  reg_errcode_t err;
+  re_node_set init_nodes;
+
+  /* Initial states have the epsilon closure of the node which is
+     the first node of the regular expression.  */
+  first = dfa->str_tree->first->node_idx;
+  dfa->init_node = first;
+  err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+
+  /* The back-references which are in initial states can epsilon transit,
+     since in this case all of the subexpressions can be null.
+     Then we add epsilon closures of the nodes which are the next nodes of
+     the back-references.  */
+  if (dfa->nbackref > 0)
+    for (i = 0; i < init_nodes.nelem; ++i)
+      {
+	int node_idx = init_nodes.elems[i];
+	re_token_type_t type = dfa->nodes[node_idx].type;
+
+	int clexp_idx;
+	if (type != OP_BACK_REF)
+	  continue;
+	for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx)
+	  {
+	    re_token_t *clexp_node;
+	    clexp_node = dfa->nodes + init_nodes.elems[clexp_idx];
+	    if (clexp_node->type == OP_CLOSE_SUBEXP
+		&& clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx)
+	      break;
+	  }
+	if (clexp_idx == init_nodes.nelem)
+	  continue;
+
+	if (type == OP_BACK_REF)
+	  {
+	    int dest_idx = dfa->edests[node_idx].elems[0];
+	    if (!re_node_set_contains (&init_nodes, dest_idx))
+	      {
+		re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx);
+		i = 0;
+	      }
+	  }
+      }
+
+  /* It must be the first time to invoke acquire_state.  */
+  dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0);
+  /* We don't check ERR here, since the initial state must not be NULL.  */
+  if (BE (dfa->init_state == NULL, 0))
+    return err;
+  if (dfa->init_state->has_constraint)
+    {
+      dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes,
+						       CONTEXT_WORD);
+      dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes,
+						     CONTEXT_NEWLINE);
+      dfa->init_state_begbuf = re_acquire_state_context (&err, dfa,
+							 &init_nodes,
+							 CONTEXT_NEWLINE
+							 | CONTEXT_BEGBUF);
+      if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+	      || dfa->init_state_begbuf == NULL, 0))
+	return err;
+    }
+  else
+    dfa->init_state_word = dfa->init_state_nl
+      = dfa->init_state_begbuf = dfa->init_state;
+
+  re_node_set_free (&init_nodes);
+  return REG_NOERROR;
+}
+

+#ifdef RE_ENABLE_I18N
+/* If it is possible to do searching in single byte encoding instead of UTF-8
+   to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change
+   DFA nodes where needed.  */
+
+static void
+optimize_utf8 (re_dfa_t *dfa)
+{
+  int node, i, mb_chars = 0, has_period = 0;
+
+  for (node = 0; node < dfa->nodes_len; ++node)
+    switch (dfa->nodes[node].type)
+      {
+      case CHARACTER:
+	if (dfa->nodes[node].opr.c >= 0x80)
+	  mb_chars = 1;
+	break;
+      case ANCHOR:
+	switch (dfa->nodes[node].opr.idx)
+	  {
+	  case LINE_FIRST:
+	  case LINE_LAST:
+	  case BUF_FIRST:
+	  case BUF_LAST:
+	    break;
+	  default:
+	    /* Word anchors etc. cannot be handled.  */
+	    return;
+	  }
+	break;
+      case OP_PERIOD:
+        has_period = 1;
+        break;
+      case OP_BACK_REF:
+      case OP_ALT:
+      case END_OF_RE:
+      case OP_DUP_ASTERISK:
+      case OP_OPEN_SUBEXP:
+      case OP_CLOSE_SUBEXP:
+	break;
+      case COMPLEX_BRACKET:
+	return;
+      case SIMPLE_BRACKET:
+	/* Just double check.  The non-ASCII range starts at 0x80.  */
+	assert (0x80 % BITSET_WORD_BITS == 0);
+        for (i = 0x80 / BITSET_WORD_BITS; i < BITSET_WORDS; ++i)
+	  if (dfa->nodes[node].opr.sbcset[i])
+	    return;
+	break;
+      default:
+	abort ();
+      }
+
+  if (mb_chars || has_period)
+    for (node = 0; node < dfa->nodes_len; ++node)
+      {
+	if (dfa->nodes[node].type == CHARACTER
+	    && dfa->nodes[node].opr.c >= 0x80)
+	  dfa->nodes[node].mb_partial = 0;
+	else if (dfa->nodes[node].type == OP_PERIOD)
+	  dfa->nodes[node].type = OP_UTF8_PERIOD;
+      }
+
+  /* The search can be in single byte locale.  */
+  dfa->mb_cur_max = 1;
+  dfa->is_utf8 = 0;
+  dfa->has_mb_node = dfa->nbackref > 0 || has_period;
+}
+#endif
+

+/* Analyze the structure tree, and calculate "first", "next", "edest",
+   "eclosure", and "inveclosure".  */
+
+static reg_errcode_t
+analyze (regex_t *preg)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  reg_errcode_t ret;
+
+  /* Allocate arrays.  */
+  dfa->nexts = re_malloc (int, dfa->nodes_alloc);
+  dfa->org_indices = re_malloc (int, dfa->nodes_alloc);
+  dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
+  dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+  if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL
+	  || dfa->eclosures == NULL, 0))
+    return REG_ESPACE;
+
+  dfa->subexp_map = re_malloc (int, preg->re_nsub);
+  if (dfa->subexp_map != NULL)
+    {
+      int i;
+      for (i = 0; i < preg->re_nsub; i++)
+	dfa->subexp_map[i] = i;
+      preorder (dfa->str_tree, optimize_subexps, dfa);
+      for (i = 0; i < preg->re_nsub; i++)
+	if (dfa->subexp_map[i] != i)
+	  break;
+      if (i == preg->re_nsub)
+	{
+	  free (dfa->subexp_map);
+	  dfa->subexp_map = NULL;
+	}
+    }
+
+  ret = postorder (dfa->str_tree, lower_subexps, preg);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  ret = postorder (dfa->str_tree, calc_first, dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  preorder (dfa->str_tree, calc_next, dfa);
+  ret = preorder (dfa->str_tree, link_nfa_nodes, dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  ret = calc_eclosure (dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  /* We only need this during the prune_impossible_nodes pass in regexec.c;
+     skip it if p_i_n will not run, as calc_inveclosure can be quadratic.  */
+  if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match)
+      || dfa->nbackref)
+    {
+      dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len);
+      if (BE (dfa->inveclosures == NULL, 0))
+        return REG_ESPACE;
+      ret = calc_inveclosure (dfa);
+    }
+
+  return ret;
+}
+
+/* Our parse trees are very unbalanced, so we cannot use a stack to
+   implement parse tree visits.  Instead, we use parent pointers and
+   some hairy code in these two functions.  */
+static reg_errcode_t
+postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+	   void *extra)
+{
+  bin_tree_t *node, *prev;
+
+  for (node = root; ; )
+    {
+      /* Descend down the tree, preferably to the left (or to the right
+	 if that's the only child).  */
+      while (node->left || node->right)
+	if (node->left)
+          node = node->left;
+        else
+          node = node->right;
+
+      do
+	{
+	  reg_errcode_t err = fn (extra, node);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+          if (node->parent == NULL)
+	    return REG_NOERROR;
+	  prev = node;
+	  node = node->parent;
+	}
+      /* Go up while we have a node that is reached from the right.  */
+      while (node->right == prev || node->right == NULL);
+      node = node->right;
+    }
+}
+
+static reg_errcode_t
+preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+	  void *extra)
+{
+  bin_tree_t *node;
+
+  for (node = root; ; )
+    {
+      reg_errcode_t err = fn (extra, node);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+
+      /* Go to the left node, or up and to the right.  */
+      if (node->left)
+	node = node->left;
+      else
+	{
+	  bin_tree_t *prev = NULL;
+	  while (node->right == prev || node->right == NULL)
+	    {
+	      prev = node;
+	      node = node->parent;
+	      if (!node)
+	        return REG_NOERROR;
+	    }
+	  node = node->right;
+	}
+    }
+}
+
+/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell
+   re_search_internal to map the inner one's opr.idx to this one's.  Adjust
+   backreferences as well.  Requires a preorder visit.  */
+static reg_errcode_t
+optimize_subexps (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+
+  if (node->token.type == OP_BACK_REF && dfa->subexp_map)
+    {
+      int idx = node->token.opr.idx;
+      node->token.opr.idx = dfa->subexp_map[idx];
+      dfa->used_bkref_map |= 1 << node->token.opr.idx;
+    }
+
+  else if (node->token.type == SUBEXP
+           && node->left && node->left->token.type == SUBEXP)
+    {
+      int other_idx = node->left->token.opr.idx;
+
+      node->left = node->left->left;
+      if (node->left)
+        node->left->parent = node;
+
+      dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx];
+      if (other_idx < BITSET_WORD_BITS)
+	  dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx);
+    }
+
+  return REG_NOERROR;
+}
+
+/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation
+   of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP.  */
+static reg_errcode_t
+lower_subexps (void *extra, bin_tree_t *node)
+{
+  regex_t *preg = (regex_t *) extra;
+  reg_errcode_t err = REG_NOERROR;
+
+  if (node->left && node->left->token.type == SUBEXP)
+    {
+      node->left = lower_subexp (&err, preg, node->left);
+      if (node->left)
+	node->left->parent = node;
+    }
+  if (node->right && node->right->token.type == SUBEXP)
+    {
+      node->right = lower_subexp (&err, preg, node->right);
+      if (node->right)
+	node->right->parent = node;
+    }
+
+  return err;
+}
+
+static bin_tree_t *
+lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *body = node->left;
+  bin_tree_t *op, *cls, *tree1, *tree;
+
+  if (preg->no_sub
+      /* We do not optimize empty subexpressions, because otherwise we may
+	 have bad CONCAT nodes with NULL children.  This is obviously not
+	 very common, so we do not lose much.  An example that triggers
+	 this case is the sed "script" /\(\)/x.  */
+      && node->left != NULL
+      && (node->token.opr.idx >= BITSET_WORD_BITS
+	  || !(dfa->used_bkref_map
+	       & ((bitset_word_t) 1 << node->token.opr.idx))))
+    return node->left;
+
+  /* Convert the SUBEXP node to the concatenation of an
+     OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP.  */
+  op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP);
+  cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP);
+  tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls;
+  tree = create_tree (dfa, op, tree1, CONCAT);
+  if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+
+  op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx;
+  op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp;
+  return tree;
+}
+
+/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton
+   nodes.  Requires a postorder visit.  */
+static reg_errcode_t
+calc_first (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+  if (node->token.type == CONCAT)
+    {
+      node->first = node->left->first;
+      node->node_idx = node->left->node_idx;
+    }
+  else
+    {
+      node->first = node;
+      node->node_idx = re_dfa_add_node (dfa, node->token);
+      if (BE (node->node_idx == -1, 0))
+        return REG_ESPACE;
+    }
+  return REG_NOERROR;
+}
+
+/* Pass 2: compute NEXT on the tree.  Preorder visit.  */
+static reg_errcode_t
+calc_next (void *extra, bin_tree_t *node)
+{
+  switch (node->token.type)
+    {
+    case OP_DUP_ASTERISK:
+      node->left->next = node;
+      break;
+    case CONCAT:
+      node->left->next = node->right->first;
+      node->right->next = node->next;
+      break;
+    default:
+      if (node->left)
+	node->left->next = node->next;
+      if (node->right)
+        node->right->next = node->next;
+      break;
+    }
+  return REG_NOERROR;
+}
+
+/* Pass 3: link all DFA nodes to their NEXT node (any order will do).  */
+static reg_errcode_t
+link_nfa_nodes (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+  int idx = node->node_idx;
+  reg_errcode_t err = REG_NOERROR;
+
+  switch (node->token.type)
+    {
+    case CONCAT:
+      break;
+
+    case END_OF_RE:
+      assert (node->next == NULL);
+      break;
+
+    case OP_DUP_ASTERISK:
+    case OP_ALT:
+      {
+	int left, right;
+	dfa->has_plural_match = 1;
+	if (node->left != NULL)
+	  left = node->left->first->node_idx;
+	else
+	  left = node->next->node_idx;
+	if (node->right != NULL)
+	  right = node->right->first->node_idx;
+	else
+	  right = node->next->node_idx;
+	assert (left > -1);
+	assert (right > -1);
+	err = re_node_set_init_2 (dfa->edests + idx, left, right);
+      }
+      break;
+
+    case ANCHOR:
+    case OP_OPEN_SUBEXP:
+    case OP_CLOSE_SUBEXP:
+      err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx);
+      break;
+
+    case OP_BACK_REF:
+      dfa->nexts[idx] = node->next->node_idx;
+      if (node->token.type == OP_BACK_REF)
+	re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]);
+      break;
+
+    default:
+      assert (!IS_EPSILON_NODE (node->token.type));
+      dfa->nexts[idx] = node->next->node_idx;
+      break;
+    }
+
+  return err;
+}
+
+/* Duplicate the epsilon closure of the node ROOT_NODE.
+   Note that duplicated nodes have constraint INIT_CONSTRAINT in addition
+   to their own constraint.  */
+
+static reg_errcode_t
+internal_function
+duplicate_node_closure (re_dfa_t *dfa, int top_org_node, int top_clone_node,
+			int root_node, unsigned int init_constraint)
+{
+  int org_node, clone_node, ret;
+  unsigned int constraint = init_constraint;
+  for (org_node = top_org_node, clone_node = top_clone_node;;)
+    {
+      int org_dest, clone_dest;
+      if (dfa->nodes[org_node].type == OP_BACK_REF)
+	{
+	  /* If the back reference epsilon-transit, its destination must
+	     also have the constraint.  Then duplicate the epsilon closure
+	     of the destination of the back reference, and store it in
+	     edests of the back reference.  */
+	  org_dest = dfa->nexts[org_node];
+	  re_node_set_empty (dfa->edests + clone_node);
+	  clone_dest = duplicate_node (dfa, org_dest, constraint);
+	  if (BE (clone_dest == -1, 0))
+	    return REG_ESPACE;
+	  dfa->nexts[clone_node] = dfa->nexts[org_node];
+	  ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+	  if (BE (ret < 0, 0))
+	    return REG_ESPACE;
+	}
+      else if (dfa->edests[org_node].nelem == 0)
+	{
+	  /* In case of the node can't epsilon-transit, don't duplicate the
+	     destination and store the original destination as the
+	     destination of the node.  */
+	  dfa->nexts[clone_node] = dfa->nexts[org_node];
+	  break;
+	}
+      else if (dfa->edests[org_node].nelem == 1)
+	{
+	  /* In case of the node can epsilon-transit, and it has only one
+	     destination.  */
+	  org_dest = dfa->edests[org_node].elems[0];
+	  re_node_set_empty (dfa->edests + clone_node);
+	  if (dfa->nodes[org_node].type == ANCHOR)
+	    {
+	      /* In case of the node has another constraint, append it.  */
+	      if (org_node == root_node && clone_node != org_node)
+		{
+		  /* ...but if the node is root_node itself, it means the
+		     epsilon closure have a loop, then tie it to the
+		     destination of the root_node.  */
+		  ret = re_node_set_insert (dfa->edests + clone_node,
+					    org_dest);
+		  if (BE (ret < 0, 0))
+		    return REG_ESPACE;
+		  break;
+		}
+	      constraint |= dfa->nodes[org_node].opr.ctx_type;
+	    }
+	  clone_dest = duplicate_node (dfa, org_dest, constraint);
+	  if (BE (clone_dest == -1, 0))
+	    return REG_ESPACE;
+	  ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+	  if (BE (ret < 0, 0))
+	    return REG_ESPACE;
+	}
+      else /* dfa->edests[org_node].nelem == 2 */
+	{
+	  /* In case of the node can epsilon-transit, and it has two
+	     destinations. In the bin_tree_t and DFA, that's '|' and '*'.   */
+	  org_dest = dfa->edests[org_node].elems[0];
+	  re_node_set_empty (dfa->edests + clone_node);
+	  /* Search for a duplicated node which satisfies the constraint.  */
+	  clone_dest = search_duplicated_node (dfa, org_dest, constraint);
+	  if (clone_dest == -1)
+	    {
+	      /* There are no such a duplicated node, create a new one.  */
+	      reg_errcode_t err;
+	      clone_dest = duplicate_node (dfa, org_dest, constraint);
+	      if (BE (clone_dest == -1, 0))
+		return REG_ESPACE;
+	      ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+	      if (BE (ret < 0, 0))
+		return REG_ESPACE;
+	      err = duplicate_node_closure (dfa, org_dest, clone_dest,
+					    root_node, constraint);
+	      if (BE (err != REG_NOERROR, 0))
+		return err;
+	    }
+	  else
+	    {
+	      /* There are a duplicated node which satisfy the constraint,
+		 use it to avoid infinite loop.  */
+	      ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+	      if (BE (ret < 0, 0))
+		return REG_ESPACE;
+	    }
+
+	  org_dest = dfa->edests[org_node].elems[1];
+	  clone_dest = duplicate_node (dfa, org_dest, constraint);
+	  if (BE (clone_dest == -1, 0))
+	    return REG_ESPACE;
+	  ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+	  if (BE (ret < 0, 0))
+	    return REG_ESPACE;
+	}
+      org_node = org_dest;
+      clone_node = clone_dest;
+    }
+  return REG_NOERROR;
+}
+
+/* Search for a node which is duplicated from the node ORG_NODE, and
+   satisfies the constraint CONSTRAINT.  */
+
+static int
+search_duplicated_node (const re_dfa_t *dfa, int org_node,
+			unsigned int constraint)
+{
+  int idx;
+  for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx)
+    {
+      if (org_node == dfa->org_indices[idx]
+	  && constraint == dfa->nodes[idx].constraint)
+	return idx; /* Found.  */
+    }
+  return -1; /* Not found.  */
+}
+
+/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT.
+   Return the index of the new node, or -1 if insufficient storage is
+   available.  */
+
+static int
+duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint)
+{
+  int dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]);
+  if (BE (dup_idx != -1, 1))
+    {
+      dfa->nodes[dup_idx].constraint = constraint;
+      if (dfa->nodes[org_idx].type == ANCHOR)
+	dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].opr.ctx_type;
+      dfa->nodes[dup_idx].duplicated = 1;
+
+      /* Store the index of the original node.  */
+      dfa->org_indices[dup_idx] = org_idx;
+    }
+  return dup_idx;
+}
+
+static reg_errcode_t
+calc_inveclosure (re_dfa_t *dfa)
+{
+  int src, idx, ret;
+  for (idx = 0; idx < dfa->nodes_len; ++idx)
+    re_node_set_init_empty (dfa->inveclosures + idx);
+
+  for (src = 0; src < dfa->nodes_len; ++src)
+    {
+      int *elems = dfa->eclosures[src].elems;
+      for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
+	{
+	  ret = re_node_set_insert_last (dfa->inveclosures + elems[idx], src);
+	  if (BE (ret == -1, 0))
+	    return REG_ESPACE;
+	}
+    }
+
+  return REG_NOERROR;
+}
+
+/* Calculate "eclosure" for all the node in DFA.  */
+
+static reg_errcode_t
+calc_eclosure (re_dfa_t *dfa)
+{
+  int node_idx, incomplete;
+#ifdef DEBUG
+  assert (dfa->nodes_len > 0);
+#endif
+  incomplete = 0;
+  /* For each nodes, calculate epsilon closure.  */
+  for (node_idx = 0; ; ++node_idx)
+    {
+      reg_errcode_t err;
+      re_node_set eclosure_elem;
+      if (node_idx == dfa->nodes_len)
+	{
+	  if (!incomplete)
+	    break;
+	  incomplete = 0;
+	  node_idx = 0;
+	}
+
+#ifdef DEBUG
+      assert (dfa->eclosures[node_idx].nelem != -1);
+#endif
+
+      /* If we have already calculated, skip it.  */
+      if (dfa->eclosures[node_idx].nelem != 0)
+	continue;
+      /* Calculate epsilon closure of `node_idx'.  */
+      err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+
+      if (dfa->eclosures[node_idx].nelem == 0)
+	{
+	  incomplete = 1;
+	  re_node_set_free (&eclosure_elem);
+	}
+    }
+  return REG_NOERROR;
+}
+
+/* Calculate epsilon closure of NODE.  */
+
+static reg_errcode_t
+calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, int node, int root)
+{
+  reg_errcode_t err;
+  unsigned int constraint;
+  int i, incomplete;
+  re_node_set eclosure;
+  incomplete = 0;
+  err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+
+  /* This indicates that we are calculating this node now.
+     We reference this value to avoid infinite loop.  */
+  dfa->eclosures[node].nelem = -1;
+
+  constraint = ((dfa->nodes[node].type == ANCHOR)
+		? dfa->nodes[node].opr.ctx_type : 0);
+  /* If the current node has constraints, duplicate all nodes.
+     Since they must inherit the constraints.  */
+  if (constraint
+      && dfa->edests[node].nelem
+      && !dfa->nodes[dfa->edests[node].elems[0]].duplicated)
+    {
+      err = duplicate_node_closure (dfa, node, node, node, constraint);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+    }
+
+  /* Expand each epsilon destination nodes.  */
+  if (IS_EPSILON_NODE(dfa->nodes[node].type))
+    for (i = 0; i < dfa->edests[node].nelem; ++i)
+      {
+	re_node_set eclosure_elem;
+	int edest = dfa->edests[node].elems[i];
+	/* If calculating the epsilon closure of `edest' is in progress,
+	   return intermediate result.  */
+	if (dfa->eclosures[edest].nelem == -1)
+	  {
+	    incomplete = 1;
+	    continue;
+	  }
+	/* If we haven't calculated the epsilon closure of `edest' yet,
+	   calculate now. Otherwise use calculated epsilon closure.  */
+	if (dfa->eclosures[edest].nelem == 0)
+	  {
+	    err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0);
+	    if (BE (err != REG_NOERROR, 0))
+	      return err;
+	  }
+	else
+	  eclosure_elem = dfa->eclosures[edest];
+	/* Merge the epsilon closure of `edest'.  */
+	re_node_set_merge (&eclosure, &eclosure_elem);
+	/* If the epsilon closure of `edest' is incomplete,
+	   the epsilon closure of this node is also incomplete.  */
+	if (dfa->eclosures[edest].nelem == 0)
+	  {
+	    incomplete = 1;
+	    re_node_set_free (&eclosure_elem);
+	  }
+      }
+
+  /* Epsilon closures include itself.  */
+  re_node_set_insert (&eclosure, node);
+  if (incomplete && !root)
+    dfa->eclosures[node].nelem = 0;
+  else
+    dfa->eclosures[node] = eclosure;
+  *new_set = eclosure;
+  return REG_NOERROR;
+}
+

+/* Functions for token which are used in the parser.  */
+
+/* Fetch a token from INPUT.
+   We must not use this function inside bracket expressions.  */
+
+static void
+internal_function
+fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax)
+{
+  re_string_skip_bytes (input, peek_token (result, input, syntax));
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+   We must not use this function inside bracket expressions.  */
+
+static int
+internal_function
+peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
+{
+  unsigned char c;
+
+  if (re_string_eoi (input))
+    {
+      token->type = END_OF_RE;
+      return 0;
+    }
+
+  c = re_string_peek_byte (input, 0);
+  token->opr.c = c;
+
+  token->word_char = 0;
+#ifdef RE_ENABLE_I18N
+  token->mb_partial = 0;
+  if (input->mb_cur_max > 1 &&
+      !re_string_first_byte (input, re_string_cur_idx (input)))
+    {
+      token->type = CHARACTER;
+      token->mb_partial = 1;
+      return 1;
+    }
+#endif
+  if (c == '\\')
+    {
+      unsigned char c2;
+      if (re_string_cur_idx (input) + 1 >= re_string_length (input))
+	{
+	  token->type = BACK_SLASH;
+	  return 1;
+	}
+
+      c2 = re_string_peek_byte_case (input, 1);
+      token->opr.c = c2;
+      token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+      if (input->mb_cur_max > 1)
+	{
+	  wint_t wc = re_string_wchar_at (input,
+					  re_string_cur_idx (input) + 1);
+	  token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+	}
+      else
+#endif
+	token->word_char = IS_WORD_CHAR (c2) != 0;
+
+      switch (c2)
+	{
+	case '|':
+	  if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR))
+	    token->type = OP_ALT;
+	  break;
+	case '1': case '2': case '3': case '4': case '5':
+	case '6': case '7': case '8': case '9':
+	  if (!(syntax & RE_NO_BK_REFS))
+	    {
+	      token->type = OP_BACK_REF;
+	      token->opr.idx = c2 - '1';
+	    }
+	  break;
+	case '<':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = WORD_FIRST;
+	    }
+	  break;
+	case '>':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = WORD_LAST;
+	    }
+	  break;
+	case 'b':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = WORD_DELIM;
+	    }
+	  break;
+	case 'B':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = NOT_WORD_DELIM;
+	    }
+	  break;
+	case 'w':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    token->type = OP_WORD;
+	  break;
+	case 'W':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    token->type = OP_NOTWORD;
+	  break;
+	case 's':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    token->type = OP_SPACE;
+	  break;
+	case 'S':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    token->type = OP_NOTSPACE;
+	  break;
+	case '`':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = BUF_FIRST;
+	    }
+	  break;
+	case '\'':
+	  if (!(syntax & RE_NO_GNU_OPS))
+	    {
+	      token->type = ANCHOR;
+	      token->opr.ctx_type = BUF_LAST;
+	    }
+	  break;
+	case '(':
+	  if (!(syntax & RE_NO_BK_PARENS))
+	    token->type = OP_OPEN_SUBEXP;
+	  break;
+	case ')':
+	  if (!(syntax & RE_NO_BK_PARENS))
+	    token->type = OP_CLOSE_SUBEXP;
+	  break;
+	case '+':
+	  if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+	    token->type = OP_DUP_PLUS;
+	  break;
+	case '?':
+	  if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+	    token->type = OP_DUP_QUESTION;
+	  break;
+	case '{':
+	  if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+	    token->type = OP_OPEN_DUP_NUM;
+	  break;
+	case '}':
+	  if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+	    token->type = OP_CLOSE_DUP_NUM;
+	  break;
+	default:
+	  break;
+	}
+      return 2;
+    }
+
+  token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+  if (input->mb_cur_max > 1)
+    {
+      wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input));
+      token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+    }
+  else
+#endif
+    token->word_char = IS_WORD_CHAR (token->opr.c);
+
+  switch (c)
+    {
+    case '\n':
+      if (syntax & RE_NEWLINE_ALT)
+	token->type = OP_ALT;
+      break;
+    case '|':
+      if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR))
+	token->type = OP_ALT;
+      break;
+    case '*':
+      token->type = OP_DUP_ASTERISK;
+      break;
+    case '+':
+      if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+	token->type = OP_DUP_PLUS;
+      break;
+    case '?':
+      if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+	token->type = OP_DUP_QUESTION;
+      break;
+    case '{':
+      if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+	token->type = OP_OPEN_DUP_NUM;
+      break;
+    case '}':
+      if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+	token->type = OP_CLOSE_DUP_NUM;
+      break;
+    case '(':
+      if (syntax & RE_NO_BK_PARENS)
+	token->type = OP_OPEN_SUBEXP;
+      break;
+    case ')':
+      if (syntax & RE_NO_BK_PARENS)
+	token->type = OP_CLOSE_SUBEXP;
+      break;
+    case '[':
+      token->type = OP_OPEN_BRACKET;
+      break;
+    case '.':
+      token->type = OP_PERIOD;
+      break;
+    case '^':
+      if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) &&
+	  re_string_cur_idx (input) != 0)
+	{
+	  char prev = re_string_peek_byte (input, -1);
+	  if (!(syntax & RE_NEWLINE_ALT) || prev != '\n')
+	    break;
+	}
+      token->type = ANCHOR;
+      token->opr.ctx_type = LINE_FIRST;
+      break;
+    case '$':
+      if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+	  re_string_cur_idx (input) + 1 != re_string_length (input))
+	{
+	  re_token_t next;
+	  re_string_skip_bytes (input, 1);
+	  peek_token (&next, input, syntax);
+	  re_string_skip_bytes (input, -1);
+	  if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP)
+	    break;
+	}
+      token->type = ANCHOR;
+      token->opr.ctx_type = LINE_LAST;
+      break;
+    default:
+      break;
+    }
+  return 1;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+   We must not use this function out of bracket expressions.  */
+
+static int
+internal_function
+peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
+{
+  unsigned char c;
+  if (re_string_eoi (input))
+    {
+      token->type = END_OF_RE;
+      return 0;
+    }
+  c = re_string_peek_byte (input, 0);
+  token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+  if (input->mb_cur_max > 1 &&
+      !re_string_first_byte (input, re_string_cur_idx (input)))
+    {
+      token->type = CHARACTER;
+      return 1;
+    }
+#endif /* RE_ENABLE_I18N */
+
+  if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS)
+      && re_string_cur_idx (input) + 1 < re_string_length (input))
+    {
+      /* In this case, '\' escape a character.  */
+      unsigned char c2;
+      re_string_skip_bytes (input, 1);
+      c2 = re_string_peek_byte (input, 0);
+      token->opr.c = c2;
+      token->type = CHARACTER;
+      return 1;
+    }
+  if (c == '[') /* '[' is a special char in a bracket exps.  */
+    {
+      unsigned char c2;
+      int token_len;
+      if (re_string_cur_idx (input) + 1 < re_string_length (input))
+	c2 = re_string_peek_byte (input, 1);
+      else
+	c2 = 0;
+      token->opr.c = c2;
+      token_len = 2;
+      switch (c2)
+	{
+	case '.':
+	  token->type = OP_OPEN_COLL_ELEM;
+	  break;
+	case '=':
+	  token->type = OP_OPEN_EQUIV_CLASS;
+	  break;
+	case ':':
+	  if (syntax & RE_CHAR_CLASSES)
+	    {
+	      token->type = OP_OPEN_CHAR_CLASS;
+	      break;
+	    }
+	  /* else fall through.  */
+	default:
+	  token->type = CHARACTER;
+	  token->opr.c = c;
+	  token_len = 1;
+	  break;
+	}
+      return token_len;
+    }
+  switch (c)
+    {
+    case '-':
+      token->type = OP_CHARSET_RANGE;
+      break;
+    case ']':
+      token->type = OP_CLOSE_BRACKET;
+      break;
+    case '^':
+      token->type = OP_NON_MATCH_LIST;
+      break;
+    default:
+      token->type = CHARACTER;
+    }
+  return 1;
+}
+

+/* Functions for parser.  */
+
+/* Entry point of the parser.
+   Parse the regular expression REGEXP and return the structure tree.
+   If an error is occured, ERR is set by error code, and return NULL.
+   This function build the following tree, from regular expression <reg_exp>:
+	   CAT
+	   / \
+	  /   \
+   <reg_exp>  EOR
+
+   CAT means concatenation.
+   EOR means end of regular expression.  */
+
+static bin_tree_t *
+parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax,
+       reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree, *eor, *root;
+  re_token_t current_token;
+  dfa->syntax = syntax;
+  fetch_token (&current_token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+  tree = parse_reg_exp (regexp, preg, &current_token, syntax, 0, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+  eor = create_tree (dfa, NULL, NULL, END_OF_RE);
+  if (tree != NULL)
+    root = create_tree (dfa, tree, eor, CONCAT);
+  else
+    root = eor;
+  if (BE (eor == NULL || root == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+  return root;
+}
+
+/* This function build the following tree, from regular expression
+   <branch1>|<branch2>:
+	   ALT
+	   / \
+	  /   \
+   <branch1> <branch2>
+
+   ALT means alternative, which represents the operator `|'.  */
+
+static bin_tree_t *
+parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+	       reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree, *branch = NULL;
+  tree = parse_branch (regexp, preg, token, syntax, nest, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+
+  while (token->type == OP_ALT)
+    {
+      fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+      if (token->type != OP_ALT && token->type != END_OF_RE
+	  && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+	{
+	  branch = parse_branch (regexp, preg, token, syntax, nest, err);
+	  if (BE (*err != REG_NOERROR && branch == NULL, 0))
+	    return NULL;
+	}
+      else
+	branch = NULL;
+      tree = create_tree (dfa, tree, branch, OP_ALT);
+      if (BE (tree == NULL, 0))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+    }
+  return tree;
+}
+
+/* This function build the following tree, from regular expression
+   <exp1><exp2>:
+	CAT
+	/ \
+       /   \
+   <exp1> <exp2>
+
+   CAT means concatenation.  */
+
+static bin_tree_t *
+parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token,
+	      reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  bin_tree_t *tree, *exp;
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  tree = parse_expression (regexp, preg, token, syntax, nest, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+
+  while (token->type != OP_ALT && token->type != END_OF_RE
+	 && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+    {
+      exp = parse_expression (regexp, preg, token, syntax, nest, err);
+      if (BE (*err != REG_NOERROR && exp == NULL, 0))
+	{
+	  return NULL;
+	}
+      if (tree != NULL && exp != NULL)
+	{
+	  tree = create_tree (dfa, tree, exp, CONCAT);
+	  if (tree == NULL)
+	    {
+	      *err = REG_ESPACE;
+	      return NULL;
+	    }
+	}
+      else if (tree == NULL)
+	tree = exp;
+      /* Otherwise exp == NULL, we don't need to create new tree.  */
+    }
+  return tree;
+}
+
+/* This function build the following tree, from regular expression a*:
+	 *
+	 |
+	 a
+*/
+
+static bin_tree_t *
+parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token,
+		  reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree;
+  switch (token->type)
+    {
+    case CHARACTER:
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+#ifdef RE_ENABLE_I18N
+      if (dfa->mb_cur_max > 1)
+	{
+	  while (!re_string_eoi (regexp)
+		 && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
+	    {
+	      bin_tree_t *mbc_remain;
+	      fetch_token (token, regexp, syntax);
+	      mbc_remain = create_token_tree (dfa, NULL, NULL, token);
+	      tree = create_tree (dfa, tree, mbc_remain, CONCAT);
+	      if (BE (mbc_remain == NULL || tree == NULL, 0))
+		{
+		  *err = REG_ESPACE;
+		  return NULL;
+		}
+	    }
+	}
+#endif
+      break;
+    case OP_OPEN_SUBEXP:
+      tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+	return NULL;
+      break;
+    case OP_OPEN_BRACKET:
+      tree = parse_bracket_exp (regexp, dfa, token, syntax, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+	return NULL;
+      break;
+    case OP_BACK_REF:
+      if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1))
+	{
+	  *err = REG_ESUBREG;
+	  return NULL;
+	}
+      dfa->used_bkref_map |= 1 << token->opr.idx;
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+      ++dfa->nbackref;
+      dfa->has_mb_node = 1;
+      break;
+    case OP_OPEN_DUP_NUM:
+      if (syntax & RE_CONTEXT_INVALID_DUP)
+	{
+	  *err = REG_BADRPT;
+	  return NULL;
+	}
+      /* FALLTHROUGH */
+    case OP_DUP_ASTERISK:
+    case OP_DUP_PLUS:
+    case OP_DUP_QUESTION:
+      if (syntax & RE_CONTEXT_INVALID_OPS)
+	{
+	  *err = REG_BADRPT;
+	  return NULL;
+	}
+      else if (syntax & RE_CONTEXT_INDEP_OPS)
+	{
+	  fetch_token (token, regexp, syntax);
+	  return parse_expression (regexp, preg, token, syntax, nest, err);
+	}
+      /* else fall through  */
+    case OP_CLOSE_SUBEXP:
+      if ((token->type == OP_CLOSE_SUBEXP) &&
+	  !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
+	{
+	  *err = REG_ERPAREN;
+	  return NULL;
+	}
+      /* else fall through  */
+    case OP_CLOSE_DUP_NUM:
+      /* We treat it as a normal character.  */
+
+      /* Then we can these characters as normal characters.  */
+      token->type = CHARACTER;
+      /* mb_partial and word_char bits should be initialized already
+	 by peek_token.  */
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+      break;
+    case ANCHOR:
+      if ((token->opr.ctx_type
+	   & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST))
+	  && dfa->word_ops_used == 0)
+	init_word_char (dfa);
+      if (token->opr.ctx_type == WORD_DELIM
+          || token->opr.ctx_type == NOT_WORD_DELIM)
+	{
+	  bin_tree_t *tree_first, *tree_last;
+	  if (token->opr.ctx_type == WORD_DELIM)
+	    {
+	      token->opr.ctx_type = WORD_FIRST;
+	      tree_first = create_token_tree (dfa, NULL, NULL, token);
+	      token->opr.ctx_type = WORD_LAST;
+            }
+          else
+            {
+	      token->opr.ctx_type = INSIDE_WORD;
+	      tree_first = create_token_tree (dfa, NULL, NULL, token);
+	      token->opr.ctx_type = INSIDE_NOTWORD;
+            }
+	  tree_last = create_token_tree (dfa, NULL, NULL, token);
+	  tree = create_tree (dfa, tree_first, tree_last, OP_ALT);
+	  if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0))
+	    {
+	      *err = REG_ESPACE;
+	      return NULL;
+	    }
+	}
+      else
+	{
+	  tree = create_token_tree (dfa, NULL, NULL, token);
+	  if (BE (tree == NULL, 0))
+	    {
+	      *err = REG_ESPACE;
+	      return NULL;
+	    }
+	}
+      /* We must return here, since ANCHORs can't be followed
+	 by repetition operators.
+	 eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
+	     it must not be "<ANCHOR(^)><REPEAT(*)>".  */
+      fetch_token (token, regexp, syntax);
+      return tree;
+    case OP_PERIOD:
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+      if (dfa->mb_cur_max > 1)
+	dfa->has_mb_node = 1;
+      break;
+    case OP_WORD:
+    case OP_NOTWORD:
+      tree = build_charclass_op (dfa, regexp->trans,
+				 (const unsigned char *) "alnum",
+				 (const unsigned char *) "_",
+				 token->type == OP_NOTWORD, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+	return NULL;
+      break;
+    case OP_SPACE:
+    case OP_NOTSPACE:
+      tree = build_charclass_op (dfa, regexp->trans,
+				 (const unsigned char *) "space",
+				 (const unsigned char *) "",
+				 token->type == OP_NOTSPACE, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+	return NULL;
+      break;
+    case OP_ALT:
+    case END_OF_RE:
+      return NULL;
+    case BACK_SLASH:
+      *err = REG_EESCAPE;
+      return NULL;
+    default:
+      /* Must not happen?  */
+#ifdef DEBUG
+      assert (0);
+#endif
+      return NULL;
+    }
+  fetch_token (token, regexp, syntax);
+
+  while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
+	 || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
+    {
+      tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+	return NULL;
+      /* In BRE consecutive duplications are not allowed.  */
+      if ((syntax & RE_CONTEXT_INVALID_DUP)
+	  && (token->type == OP_DUP_ASTERISK
+	      || token->type == OP_OPEN_DUP_NUM))
+	{
+	  *err = REG_BADRPT;
+	  return NULL;
+	}
+    }
+
+  return tree;
+}
+
+/* This function build the following tree, from regular expression
+   (<reg_exp>):
+	 SUBEXP
+	    |
+	<reg_exp>
+*/
+
+static bin_tree_t *
+parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+	       reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree;
+  size_t cur_nsub;
+  cur_nsub = preg->re_nsub++;
+
+  fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+
+  /* The subexpression may be a null string.  */
+  if (token->type == OP_CLOSE_SUBEXP)
+    tree = NULL;
+  else
+    {
+      tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
+      if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0))
+        *err = REG_EPAREN;
+      if (BE (*err != REG_NOERROR, 0))
+	return NULL;
+    }
+
+  if (cur_nsub <= '9' - '1')
+    dfa->completed_bkref_map |= 1 << cur_nsub;
+
+  tree = create_tree (dfa, tree, NULL, SUBEXP);
+  if (BE (tree == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+  tree->token.opr.idx = cur_nsub;
+  return tree;
+}
+
+/* This function parse repetition operators like "*", "+", "{1,3}" etc.  */
+
+static bin_tree_t *
+parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa,
+	      re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err)
+{
+  bin_tree_t *tree = NULL, *old_tree = NULL;
+  int i, start, end, start_idx = re_string_cur_idx (regexp);
+  re_token_t start_token = *token;
+
+  if (token->type == OP_OPEN_DUP_NUM)
+    {
+      end = 0;
+      start = fetch_number (regexp, token, syntax);
+      if (start == -1)
+	{
+	  if (token->type == CHARACTER && token->opr.c == ',')
+	    start = 0; /* We treat "{,m}" as "{0,m}".  */
+	  else
+	    {
+	      *err = REG_BADBR; /* <re>{} is invalid.  */
+	      return NULL;
+	    }
+	}
+      if (BE (start != -2, 1))
+	{
+	  /* We treat "{n}" as "{n,n}".  */
+	  end = ((token->type == OP_CLOSE_DUP_NUM) ? start
+		 : ((token->type == CHARACTER && token->opr.c == ',')
+		    ? fetch_number (regexp, token, syntax) : -2));
+	}
+      if (BE (start == -2 || end == -2, 0))
+	{
+	  /* Invalid sequence.  */
+	  if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
+	    {
+	      if (token->type == END_OF_RE)
+		*err = REG_EBRACE;
+	      else
+		*err = REG_BADBR;
+
+	      return NULL;
+	    }
+
+	  /* If the syntax bit is set, rollback.  */
+	  re_string_set_index (regexp, start_idx);
+	  *token = start_token;
+	  token->type = CHARACTER;
+	  /* mb_partial and word_char bits should be already initialized by
+	     peek_token.  */
+	  return elem;
+	}
+
+      if (BE (end != -1 && start > end, 0))
+	{
+	  /* First number greater than second.  */
+	  *err = REG_BADBR;
+	  return NULL;
+	}
+    }
+  else
+    {
+      start = (token->type == OP_DUP_PLUS) ? 1 : 0;
+      end = (token->type == OP_DUP_QUESTION) ? 1 : -1;
+    }
+
+  fetch_token (token, regexp, syntax);
+
+  if (BE (elem == NULL, 0))
+    return NULL;
+  if (BE (start == 0 && end == 0, 0))
+    {
+      postorder (elem, free_tree, NULL);
+      return NULL;
+    }
+
+  /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}".  */
+  if (BE (start > 0, 0))
+    {
+      tree = elem;
+      for (i = 2; i <= start; ++i)
+	{
+	  elem = duplicate_tree (elem, dfa);
+	  tree = create_tree (dfa, tree, elem, CONCAT);
+	  if (BE (elem == NULL || tree == NULL, 0))
+	    goto parse_dup_op_espace;
+	}
+
+      if (start == end)
+	return tree;
+
+      /* Duplicate ELEM before it is marked optional.  */
+      elem = duplicate_tree (elem, dfa);
+      old_tree = tree;
+    }
+  else
+    old_tree = NULL;
+
+  if (elem->token.type == SUBEXP)
+    postorder (elem, mark_opt_subexp, (void *) (long) elem->token.opr.idx);
+
+  tree = create_tree (dfa, elem, NULL, (end == -1 ? OP_DUP_ASTERISK : OP_ALT));
+  if (BE (tree == NULL, 0))
+    goto parse_dup_op_espace;
+
+  /* This loop is actually executed only when end != -1,
+     to rewrite <re>{0,n} as (<re>(<re>...<re>?)?)?...  We have
+     already created the start+1-th copy.  */
+  for (i = start + 2; i <= end; ++i)
+    {
+      elem = duplicate_tree (elem, dfa);
+      tree = create_tree (dfa, tree, elem, CONCAT);
+      if (BE (elem == NULL || tree == NULL, 0))
+        goto parse_dup_op_espace;
+
+      tree = create_tree (dfa, tree, NULL, OP_ALT);
+      if (BE (tree == NULL, 0))
+        goto parse_dup_op_espace;
+    }
+
+  if (old_tree)
+    tree = create_tree (dfa, old_tree, tree, CONCAT);
+
+  return tree;
+
+ parse_dup_op_espace:
+  *err = REG_ESPACE;
+  return NULL;
+}
+
+/* Size of the names for collating symbol/equivalence_class/character_class.
+   I'm not sure, but maybe enough.  */
+#define BRACKET_NAME_BUF_SIZE 32
+
+#ifndef _LIBC
+  /* Local function for parse_bracket_exp only used in case of NOT _LIBC.
+     Build the range expression which starts from START_ELEM, and ends
+     at END_ELEM.  The result are written to MBCSET and SBCSET.
+     RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+     mbcset->range_ends, is a pointer argument sinse we may
+     update it.  */
+
+static reg_errcode_t
+internal_function
+# ifdef RE_ENABLE_I18N
+build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc,
+		 bracket_elem_t *start_elem, bracket_elem_t *end_elem)
+# else /* not RE_ENABLE_I18N */
+build_range_exp (bitset_t sbcset, bracket_elem_t *start_elem,
+		 bracket_elem_t *end_elem)
+# endif /* not RE_ENABLE_I18N */
+{
+  unsigned int start_ch, end_ch;
+  /* Equivalence Classes and Character Classes can't be a range start/end.  */
+  if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+	  || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+	  0))
+    return REG_ERANGE;
+
+  /* We can handle no multi character collating elements without libc
+     support.  */
+  if (BE ((start_elem->type == COLL_SYM
+	   && strlen ((char *) start_elem->opr.name) > 1)
+	  || (end_elem->type == COLL_SYM
+	      && strlen ((char *) end_elem->opr.name) > 1), 0))
+    return REG_ECOLLATE;
+
+# ifdef RE_ENABLE_I18N
+  {
+    wchar_t wc;
+    wint_t start_wc;
+    wint_t end_wc;
+    wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+
+    start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
+		: ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+		   : 0));
+    end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch
+	      : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+		 : 0));
+    start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
+		? __btowc (start_ch) : start_elem->opr.wch);
+    end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
+	      ? __btowc (end_ch) : end_elem->opr.wch);
+    if (start_wc == WEOF || end_wc == WEOF)
+      return REG_ECOLLATE;
+    cmp_buf[0] = start_wc;
+    cmp_buf[4] = end_wc;
+    if (wcscoll (cmp_buf, cmp_buf + 4) > 0)
+      return REG_ERANGE;
+
+    /* Got valid collation sequence values, add them as a new entry.
+       However, for !_LIBC we have no collation elements: if the
+       character set is single byte, the single byte character set
+       that we build below suffices.  parse_bracket_exp passes
+       no MBCSET if dfa->mb_cur_max == 1.  */
+    if (mbcset)
+      {
+        /* Check the space of the arrays.  */
+        if (BE (*range_alloc == mbcset->nranges, 0))
+          {
+	    /* There is not enough space, need realloc.  */
+	    wchar_t *new_array_start, *new_array_end;
+	    int new_nranges;
+
+	    /* +1 in case of mbcset->nranges is 0.  */
+	    new_nranges = 2 * mbcset->nranges + 1;
+	    /* Use realloc since mbcset->range_starts and mbcset->range_ends
+	       are NULL if *range_alloc == 0.  */
+	    new_array_start = re_realloc (mbcset->range_starts, wchar_t,
+				          new_nranges);
+	    new_array_end = re_realloc (mbcset->range_ends, wchar_t,
+				        new_nranges);
+
+	    if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+	      return REG_ESPACE;
+
+	    mbcset->range_starts = new_array_start;
+	    mbcset->range_ends = new_array_end;
+	    *range_alloc = new_nranges;
+          }
+
+        mbcset->range_starts[mbcset->nranges] = start_wc;
+        mbcset->range_ends[mbcset->nranges++] = end_wc;
+      }
+
+    /* Build the table for single byte characters.  */
+    for (wc = 0; wc < SBC_MAX; ++wc)
+      {
+	cmp_buf[2] = wc;
+	if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+	    && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+	  bitset_set (sbcset, wc);
+      }
+  }
+# else /* not RE_ENABLE_I18N */
+  {
+    unsigned int ch;
+    start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch
+		: ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+		   : 0));
+    end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch
+	      : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+		 : 0));
+    if (start_ch > end_ch)
+      return REG_ERANGE;
+    /* Build the table for single byte characters.  */
+    for (ch = 0; ch < SBC_MAX; ++ch)
+      if (start_ch <= ch  && ch <= end_ch)
+	bitset_set (sbcset, ch);
+  }
+# endif /* not RE_ENABLE_I18N */
+  return REG_NOERROR;
+}
+#endif /* not _LIBC */
+
+#ifndef _LIBC
+/* Helper function for parse_bracket_exp only used in case of NOT _LIBC..
+   Build the collating element which is represented by NAME.
+   The result are written to MBCSET and SBCSET.
+   COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+   pointer argument since we may update it.  */
+
+static reg_errcode_t
+internal_function
+# ifdef RE_ENABLE_I18N
+build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset,
+			int *coll_sym_alloc, const unsigned char *name)
+# else /* not RE_ENABLE_I18N */
+build_collating_symbol (bitset_t sbcset, const unsigned char *name)
+# endif /* not RE_ENABLE_I18N */
+{
+  size_t name_len = strlen ((const char *) name);
+  if (BE (name_len != 1, 0))
+    return REG_ECOLLATE;
+  else
+    {
+      bitset_set (sbcset, name[0]);
+      return REG_NOERROR;
+    }
+}
+#endif /* not _LIBC */
+
+/* This function parse bracket expression like "[abc]", "[a-c]",
+   "[[.a-a.]]" etc.  */
+
+static bin_tree_t *
+parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
+		   reg_syntax_t syntax, reg_errcode_t *err)
+{
+#ifdef _LIBC
+  const unsigned char *collseqmb;
+  const char *collseqwc;
+  uint32_t nrules;
+  int32_t table_size;
+  const int32_t *symb_table;
+  const unsigned char *extra;
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Seek the collating symbol entry correspondings to NAME.
+     Return the index of the symbol in the SYMB_TABLE.  */
+
+  auto inline int32_t
+  __attribute ((always_inline))
+  seek_collating_symbol_entry (name, name_len)
+	 const unsigned char *name;
+	 size_t name_len;
+    {
+      int32_t hash = elem_hash ((const char *) name, name_len);
+      int32_t elem = hash % table_size;
+      if (symb_table[2 * elem] != 0)
+	{
+	  int32_t second = hash % (table_size - 2) + 1;
+
+	  do
+	    {
+	      /* First compare the hashing value.  */
+	      if (symb_table[2 * elem] == hash
+		  /* Compare the length of the name.  */
+		  && name_len == extra[symb_table[2 * elem + 1]]
+		  /* Compare the name.  */
+		  && memcmp (name, &extra[symb_table[2 * elem + 1] + 1],
+			     name_len) == 0)
+		{
+		  /* Yep, this is the entry.  */
+		  break;
+		}
+
+	      /* Next entry.  */
+	      elem += second;
+	    }
+	  while (symb_table[2 * elem] != 0);
+	}
+      return elem;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environment.
+     Look up the collation sequence value of BR_ELEM.
+     Return the value if succeeded, UINT_MAX otherwise.  */
+
+  auto inline unsigned int
+  __attribute ((always_inline))
+  lookup_collation_sequence_value (br_elem)
+	 bracket_elem_t *br_elem;
+    {
+      if (br_elem->type == SB_CHAR)
+	{
+	  /*
+	  if (MB_CUR_MAX == 1)
+	  */
+	  if (nrules == 0)
+	    return collseqmb[br_elem->opr.ch];
+	  else
+	    {
+	      wint_t wc = __btowc (br_elem->opr.ch);
+	      return __collseq_table_lookup (collseqwc, wc);
+	    }
+	}
+      else if (br_elem->type == MB_CHAR)
+	{
+	  if (nrules != 0)
+	    return __collseq_table_lookup (collseqwc, br_elem->opr.wch);
+	}
+      else if (br_elem->type == COLL_SYM)
+	{
+	  size_t sym_name_len = strlen ((char *) br_elem->opr.name);
+	  if (nrules != 0)
+	    {
+	      int32_t elem, idx;
+	      elem = seek_collating_symbol_entry (br_elem->opr.name,
+						  sym_name_len);
+	      if (symb_table[2 * elem] != 0)
+		{
+		  /* We found the entry.  */
+		  idx = symb_table[2 * elem + 1];
+		  /* Skip the name of collating element name.  */
+		  idx += 1 + extra[idx];
+		  /* Skip the byte sequence of the collating element.  */
+		  idx += 1 + extra[idx];
+		  /* Adjust for the alignment.  */
+		  idx = (idx + 3) & ~3;
+		  /* Skip the multibyte collation sequence value.  */
+		  idx += sizeof (unsigned int);
+		  /* Skip the wide char sequence of the collating element.  */
+		  idx += sizeof (unsigned int) *
+		    (1 + *(unsigned int *) (extra + idx));
+		  /* Return the collation sequence value.  */
+		  return *(unsigned int *) (extra + idx);
+		}
+	      else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
+		{
+		  /* No valid character.  Match it as a single byte
+		     character.  */
+		  return collseqmb[br_elem->opr.name[0]];
+		}
+	    }
+	  else if (sym_name_len == 1)
+	    return collseqmb[br_elem->opr.name[0]];
+	}
+      return UINT_MAX;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Build the range expression which starts from START_ELEM, and ends
+     at END_ELEM.  The result are written to MBCSET and SBCSET.
+     RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+     mbcset->range_ends, is a pointer argument sinse we may
+     update it.  */
+
+  auto inline reg_errcode_t
+  __attribute ((always_inline))
+  build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
+	 re_charset_t *mbcset;
+	 int *range_alloc;
+	 bitset_t sbcset;
+	 bracket_elem_t *start_elem, *end_elem;
+    {
+      unsigned int ch;
+      uint32_t start_collseq;
+      uint32_t end_collseq;
+
+      /* Equivalence Classes and Character Classes can't be a range
+	 start/end.  */
+      if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+	      || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+	      0))
+	return REG_ERANGE;
+
+      start_collseq = lookup_collation_sequence_value (start_elem);
+      end_collseq = lookup_collation_sequence_value (end_elem);
+      /* Check start/end collation sequence values.  */
+      if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0))
+	return REG_ECOLLATE;
+      if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0))
+	return REG_ERANGE;
+
+      /* Got valid collation sequence values, add them as a new entry.
+	 However, if we have no collation elements, and the character set
+	 is single byte, the single byte character set that we
+	 build below suffices. */
+      if (nrules > 0 || dfa->mb_cur_max > 1)
+	{
+          /* Check the space of the arrays.  */
+          if (BE (*range_alloc == mbcset->nranges, 0))
+	    {
+	      /* There is not enough space, need realloc.  */
+	      uint32_t *new_array_start;
+	      uint32_t *new_array_end;
+	      int new_nranges;
+
+	      /* +1 in case of mbcset->nranges is 0.  */
+	      new_nranges = 2 * mbcset->nranges + 1;
+	      new_array_start = re_realloc (mbcset->range_starts, uint32_t,
+					    new_nranges);
+	      new_array_end = re_realloc (mbcset->range_ends, uint32_t,
+				          new_nranges);
+
+	      if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+	        return REG_ESPACE;
+
+	      mbcset->range_starts = new_array_start;
+	      mbcset->range_ends = new_array_end;
+	      *range_alloc = new_nranges;
+	    }
+
+          mbcset->range_starts[mbcset->nranges] = start_collseq;
+          mbcset->range_ends[mbcset->nranges++] = end_collseq;
+	}
+
+      /* Build the table for single byte characters.  */
+      for (ch = 0; ch < SBC_MAX; ch++)
+	{
+	  uint32_t ch_collseq;
+	  /*
+	  if (MB_CUR_MAX == 1)
+	  */
+	  if (nrules == 0)
+	    ch_collseq = collseqmb[ch];
+	  else
+	    ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch));
+	  if (start_collseq <= ch_collseq && ch_collseq <= end_collseq)
+	    bitset_set (sbcset, ch);
+	}
+      return REG_NOERROR;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Build the collating element which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+     pointer argument sinse we may update it.  */
+
+  auto inline reg_errcode_t
+  __attribute ((always_inline))
+  build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
+	 re_charset_t *mbcset;
+	 int *coll_sym_alloc;
+	 bitset_t sbcset;
+	 const unsigned char *name;
+    {
+      int32_t elem, idx;
+      size_t name_len = strlen ((const char *) name);
+      if (nrules != 0)
+	{
+	  elem = seek_collating_symbol_entry (name, name_len);
+	  if (symb_table[2 * elem] != 0)
+	    {
+	      /* We found the entry.  */
+	      idx = symb_table[2 * elem + 1];
+	      /* Skip the name of collating element name.  */
+	      idx += 1 + extra[idx];
+	    }
+	  else if (symb_table[2 * elem] == 0 && name_len == 1)
+	    {
+	      /* No valid character, treat it as a normal
+		 character.  */
+	      bitset_set (sbcset, name[0]);
+	      return REG_NOERROR;
+	    }
+	  else
+	    return REG_ECOLLATE;
+
+	  /* Got valid collation sequence, add it as a new entry.  */
+	  /* Check the space of the arrays.  */
+	  if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0))
+	    {
+	      /* Not enough, realloc it.  */
+	      /* +1 in case of mbcset->ncoll_syms is 0.  */
+	      int new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
+	      /* Use realloc since mbcset->coll_syms is NULL
+		 if *alloc == 0.  */
+	      int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t,
+						   new_coll_sym_alloc);
+	      if (BE (new_coll_syms == NULL, 0))
+		return REG_ESPACE;
+	      mbcset->coll_syms = new_coll_syms;
+	      *coll_sym_alloc = new_coll_sym_alloc;
+	    }
+	  mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
+	  return REG_NOERROR;
+	}
+      else
+	{
+	  if (BE (name_len != 1, 0))
+	    return REG_ECOLLATE;
+	  else
+	    {
+	      bitset_set (sbcset, name[0]);
+	      return REG_NOERROR;
+	    }
+	}
+    }
+#endif
+
+  re_token_t br_token;
+  re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+  re_charset_t *mbcset;
+  int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
+  int equiv_class_alloc = 0, char_class_alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+  int non_match = 0;
+  bin_tree_t *work_tree;
+  int token_len;
+  int first_round = 1;
+#ifdef _LIBC
+  collseqmb = (const unsigned char *)
+    _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+  nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules)
+    {
+      /*
+      if (MB_CUR_MAX > 1)
+      */
+      collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+      table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB);
+      symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+						  _NL_COLLATE_SYMB_TABLEMB);
+      extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+						   _NL_COLLATE_SYMB_EXTRAMB);
+    }
+#endif
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+#ifdef RE_ENABLE_I18N
+  mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+#ifdef RE_ENABLE_I18N
+  if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else
+  if (BE (sbcset == NULL, 0))
+#endif /* RE_ENABLE_I18N */
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+
+  token_len = peek_token_bracket (token, regexp, syntax);
+  if (BE (token->type == END_OF_RE, 0))
+    {
+      *err = REG_BADPAT;
+      goto parse_bracket_exp_free_return;
+    }
+  if (token->type == OP_NON_MATCH_LIST)
+    {
+#ifdef RE_ENABLE_I18N
+      mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+      non_match = 1;
+      if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+	bitset_set (sbcset, '\n');
+      re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+      token_len = peek_token_bracket (token, regexp, syntax);
+      if (BE (token->type == END_OF_RE, 0))
+	{
+	  *err = REG_BADPAT;
+	  goto parse_bracket_exp_free_return;
+	}
+    }
+
+  /* We treat the first ']' as a normal character.  */
+  if (token->type == OP_CLOSE_BRACKET)
+    token->type = CHARACTER;
+
+  while (1)
+    {
+      bracket_elem_t start_elem, end_elem;
+      unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
+      unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
+      reg_errcode_t ret;
+      int token_len2 = 0, is_range_exp = 0;
+      re_token_t token2;
+
+      start_elem.opr.name = start_name_buf;
+      ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
+				   syntax, first_round);
+      if (BE (ret != REG_NOERROR, 0))
+	{
+	  *err = ret;
+	  goto parse_bracket_exp_free_return;
+	}
+      first_round = 0;
+
+      /* Get information about the next token.  We need it in any case.  */
+      token_len = peek_token_bracket (token, regexp, syntax);
+
+      /* Do not check for ranges if we know they are not allowed.  */
+      if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS)
+	{
+	  if (BE (token->type == END_OF_RE, 0))
+	    {
+	      *err = REG_EBRACK;
+	      goto parse_bracket_exp_free_return;
+	    }
+	  if (token->type == OP_CHARSET_RANGE)
+	    {
+	      re_string_skip_bytes (regexp, token_len); /* Skip '-'.  */
+	      token_len2 = peek_token_bracket (&token2, regexp, syntax);
+	      if (BE (token2.type == END_OF_RE, 0))
+		{
+		  *err = REG_EBRACK;
+		  goto parse_bracket_exp_free_return;
+		}
+	      if (token2.type == OP_CLOSE_BRACKET)
+		{
+		  /* We treat the last '-' as a normal character.  */
+		  re_string_skip_bytes (regexp, -token_len);
+		  token->type = CHARACTER;
+		}
+	      else
+		is_range_exp = 1;
+	    }
+	}
+
+      if (is_range_exp == 1)
+	{
+	  end_elem.opr.name = end_name_buf;
+	  ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
+				       dfa, syntax, 1);
+	  if (BE (ret != REG_NOERROR, 0))
+	    {
+	      *err = ret;
+	      goto parse_bracket_exp_free_return;
+	    }
+
+	  token_len = peek_token_bracket (token, regexp, syntax);
+
+#ifdef _LIBC
+	  *err = build_range_exp (sbcset, mbcset, &range_alloc,
+				  &start_elem, &end_elem);
+#else
+# ifdef RE_ENABLE_I18N
+	  *err = build_range_exp (sbcset,
+				  dfa->mb_cur_max > 1 ? mbcset : NULL,
+				  &range_alloc, &start_elem, &end_elem);
+# else
+	  *err = build_range_exp (sbcset, &start_elem, &end_elem);
+# endif
+#endif /* RE_ENABLE_I18N */
+	  if (BE (*err != REG_NOERROR, 0))
+	    goto parse_bracket_exp_free_return;
+	}
+      else
+	{
+	  switch (start_elem.type)
+	    {
+	    case SB_CHAR:
+	      bitset_set (sbcset, start_elem.opr.ch);
+	      break;
+#ifdef RE_ENABLE_I18N
+	    case MB_CHAR:
+	      /* Check whether the array has enough space.  */
+	      if (BE (mbchar_alloc == mbcset->nmbchars, 0))
+		{
+		  wchar_t *new_mbchars;
+		  /* Not enough, realloc it.  */
+		  /* +1 in case of mbcset->nmbchars is 0.  */
+		  mbchar_alloc = 2 * mbcset->nmbchars + 1;
+		  /* Use realloc since array is NULL if *alloc == 0.  */
+		  new_mbchars = re_realloc (mbcset->mbchars, wchar_t,
+					    mbchar_alloc);
+		  if (BE (new_mbchars == NULL, 0))
+		    goto parse_bracket_exp_espace;
+		  mbcset->mbchars = new_mbchars;
+		}
+	      mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
+	      break;
+#endif /* RE_ENABLE_I18N */
+	    case EQUIV_CLASS:
+	      *err = build_equiv_class (sbcset,
+#ifdef RE_ENABLE_I18N
+					mbcset, &equiv_class_alloc,
+#endif /* RE_ENABLE_I18N */
+					start_elem.opr.name);
+	      if (BE (*err != REG_NOERROR, 0))
+		goto parse_bracket_exp_free_return;
+	      break;
+	    case COLL_SYM:
+	      *err = build_collating_symbol (sbcset,
+#ifdef RE_ENABLE_I18N
+					     mbcset, &coll_sym_alloc,
+#endif /* RE_ENABLE_I18N */
+					     start_elem.opr.name);
+	      if (BE (*err != REG_NOERROR, 0))
+		goto parse_bracket_exp_free_return;
+	      break;
+	    case CHAR_CLASS:
+	      *err = build_charclass (regexp->trans, sbcset,
+#ifdef RE_ENABLE_I18N
+				      mbcset, &char_class_alloc,
+#endif /* RE_ENABLE_I18N */
+				      start_elem.opr.name, syntax);
+	      if (BE (*err != REG_NOERROR, 0))
+	       goto parse_bracket_exp_free_return;
+	      break;
+	    default:
+	      assert (0);
+	      break;
+	    }
+	}
+      if (BE (token->type == END_OF_RE, 0))
+	{
+	  *err = REG_EBRACK;
+	  goto parse_bracket_exp_free_return;
+	}
+      if (token->type == OP_CLOSE_BRACKET)
+	break;
+    }
+
+  re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+
+  /* If it is non-matching list.  */
+  if (non_match)
+    bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+  /* Ensure only single byte characters are set.  */
+  if (dfa->mb_cur_max > 1)
+    bitset_mask (sbcset, dfa->sb_char);
+
+  if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
+      || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes
+						     || mbcset->non_match)))
+    {
+      bin_tree_t *mbc_tree;
+      int sbc_idx;
+      /* Build a tree for complex bracket.  */
+      dfa->has_mb_node = 1;
+      br_token.type = COMPLEX_BRACKET;
+      br_token.opr.mbcset = mbcset;
+      mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (mbc_tree == NULL, 0))
+	goto parse_bracket_exp_espace;
+      for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx)
+	if (sbcset[sbc_idx])
+	  break;
+      /* If there are no bits set in sbcset, there is no point
+	 of having both SIMPLE_BRACKET and COMPLEX_BRACKET.  */
+      if (sbc_idx < BITSET_WORDS)
+	{
+          /* Build a tree for simple bracket.  */
+          br_token.type = SIMPLE_BRACKET;
+          br_token.opr.sbcset = sbcset;
+          work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+          if (BE (work_tree == NULL, 0))
+            goto parse_bracket_exp_espace;
+
+          /* Then join them by ALT node.  */
+          work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT);
+          if (BE (work_tree == NULL, 0))
+            goto parse_bracket_exp_espace;
+	}
+      else
+	{
+	  re_free (sbcset);
+	  work_tree = mbc_tree;
+	}
+    }
+  else
+#endif /* not RE_ENABLE_I18N */
+    {
+#ifdef RE_ENABLE_I18N
+      free_charset (mbcset);
+#endif
+      /* Build a tree for simple bracket.  */
+      br_token.type = SIMPLE_BRACKET;
+      br_token.opr.sbcset = sbcset;
+      work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (work_tree == NULL, 0))
+        goto parse_bracket_exp_espace;
+    }
+  return work_tree;
+
+ parse_bracket_exp_espace:
+  *err = REG_ESPACE;
+ parse_bracket_exp_free_return:
+  re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+  free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+  return NULL;
+}
+
+/* Parse an element in the bracket expression.  */
+
+static reg_errcode_t
+parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp,
+		       re_token_t *token, int token_len, re_dfa_t *dfa,
+		       reg_syntax_t syntax, int accept_hyphen)
+{
+#ifdef RE_ENABLE_I18N
+  int cur_char_size;
+  cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp));
+  if (cur_char_size > 1)
+    {
+      elem->type = MB_CHAR;
+      elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp));
+      re_string_skip_bytes (regexp, cur_char_size);
+      return REG_NOERROR;
+    }
+#endif /* RE_ENABLE_I18N */
+  re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+  if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS
+      || token->type == OP_OPEN_EQUIV_CLASS)
+    return parse_bracket_symbol (elem, regexp, token);
+  if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen)
+    {
+      /* A '-' must only appear as anything but a range indicator before
+	 the closing bracket.  Everything else is an error.  */
+      re_token_t token2;
+      (void) peek_token_bracket (&token2, regexp, syntax);
+      if (token2.type != OP_CLOSE_BRACKET)
+	/* The actual error value is not standardized since this whole
+	   case is undefined.  But ERANGE makes good sense.  */
+	return REG_ERANGE;
+    }
+  elem->type = SB_CHAR;
+  elem->opr.ch = token->opr.c;
+  return REG_NOERROR;
+}
+
+/* Parse a bracket symbol in the bracket expression.  Bracket symbols are
+   such as [:<character_class>:], [.<collating_element>.], and
+   [=<equivalent_class>=].  */
+
+static reg_errcode_t
+parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp,
+		      re_token_t *token)
+{
+  unsigned char ch, delim = token->opr.c;
+  int i = 0;
+  if (re_string_eoi(regexp))
+    return REG_EBRACK;
+  for (;; ++i)
+    {
+      if (i >= BRACKET_NAME_BUF_SIZE)
+	return REG_EBRACK;
+      if (token->type == OP_OPEN_CHAR_CLASS)
+	ch = re_string_fetch_byte_case (regexp);
+      else
+	ch = re_string_fetch_byte (regexp);
+      if (re_string_eoi(regexp))
+	return REG_EBRACK;
+      if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
+	break;
+      elem->opr.name[i] = ch;
+    }
+  re_string_skip_bytes (regexp, 1);
+  elem->opr.name[i] = '\0';
+  switch (token->type)
+    {
+    case OP_OPEN_COLL_ELEM:
+      elem->type = COLL_SYM;
+      break;
+    case OP_OPEN_EQUIV_CLASS:
+      elem->type = EQUIV_CLASS;
+      break;
+    case OP_OPEN_CHAR_CLASS:
+      elem->type = CHAR_CLASS;
+      break;
+    default:
+      break;
+    }
+  return REG_NOERROR;
+}
+
+  /* Helper function for parse_bracket_exp.
+     Build the equivalence class which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
+     is a pointer argument sinse we may update it.  */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_equiv_class (bitset_t sbcset, re_charset_t *mbcset,
+		   int *equiv_class_alloc, const unsigned char *name)
+#else /* not RE_ENABLE_I18N */
+build_equiv_class (bitset_t sbcset, const unsigned char *name)
+#endif /* not RE_ENABLE_I18N */
+{
+#ifdef _LIBC
+  uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules != 0)
+    {
+      const int32_t *table, *indirect;
+      const unsigned char *weights, *extra, *cp;
+      unsigned char char_buf[2];
+      int32_t idx1, idx2;
+      unsigned int ch;
+      size_t len;
+      /* This #include defines a local function!  */
+# include <locale/weight.h>
+      /* Calculate the index for equivalence class.  */
+      cp = name;
+      table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+      weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+					       _NL_COLLATE_WEIGHTMB);
+      extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+						   _NL_COLLATE_EXTRAMB);
+      indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+						_NL_COLLATE_INDIRECTMB);
+      idx1 = findidx (&cp);
+      if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0))
+	/* This isn't a valid character.  */
+	return REG_ECOLLATE;
+
+      /* Build single byte matcing table for this equivalence class.  */
+      char_buf[1] = (unsigned char) '\0';
+      len = weights[idx1 & 0xffffff];
+      for (ch = 0; ch < SBC_MAX; ++ch)
+	{
+	  char_buf[0] = ch;
+	  cp = char_buf;
+	  idx2 = findidx (&cp);
+/*
+	  idx2 = table[ch];
+*/
+	  if (idx2 == 0)
+	    /* This isn't a valid character.  */
+	    continue;
+	  /* Compare only if the length matches and the collation rule
+	     index is the same.  */
+	  if (len == weights[idx2 & 0xffffff] && (idx1 >> 24) == (idx2 >> 24))
+	    {
+	      int cnt = 0;
+
+	      while (cnt <= len &&
+		     weights[(idx1 & 0xffffff) + 1 + cnt]
+		     == weights[(idx2 & 0xffffff) + 1 + cnt])
+		++cnt;
+
+	      if (cnt > len)
+		bitset_set (sbcset, ch);
+	    }
+	}
+      /* Check whether the array has enough space.  */
+      if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0))
+	{
+	  /* Not enough, realloc it.  */
+	  /* +1 in case of mbcset->nequiv_classes is 0.  */
+	  int new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
+	  /* Use realloc since the array is NULL if *alloc == 0.  */
+	  int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes,
+						   int32_t,
+						   new_equiv_class_alloc);
+	  if (BE (new_equiv_classes == NULL, 0))
+	    return REG_ESPACE;
+	  mbcset->equiv_classes = new_equiv_classes;
+	  *equiv_class_alloc = new_equiv_class_alloc;
+	}
+      mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
+    }
+  else
+#endif /* _LIBC */
+    {
+      if (BE (strlen ((const char *) name) != 1, 0))
+	return REG_ECOLLATE;
+      bitset_set (sbcset, *name);
+    }
+  return REG_NOERROR;
+}
+
+  /* Helper function for parse_bracket_exp.
+     Build the character class which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
+     is a pointer argument sinse we may update it.  */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+		 re_charset_t *mbcset, int *char_class_alloc,
+		 const unsigned char *class_name, reg_syntax_t syntax)
+#else /* not RE_ENABLE_I18N */
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+		 const unsigned char *class_name, reg_syntax_t syntax)
+#endif /* not RE_ENABLE_I18N */
+{
+  int i;
+  const char *name = (const char *) class_name;
+
+  /* In case of REG_ICASE "upper" and "lower" match the both of
+     upper and lower cases.  */
+  if ((syntax & RE_ICASE)
+      && (strcmp (name, "upper") == 0 || strcmp (name, "lower") == 0))
+    name = "alpha";
+
+#ifdef RE_ENABLE_I18N
+  /* Check the space of the arrays.  */
+  if (BE (*char_class_alloc == mbcset->nchar_classes, 0))
+    {
+      /* Not enough, realloc it.  */
+      /* +1 in case of mbcset->nchar_classes is 0.  */
+      int new_char_class_alloc = 2 * mbcset->nchar_classes + 1;
+      /* Use realloc since array is NULL if *alloc == 0.  */
+      wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t,
+					       new_char_class_alloc);
+      if (BE (new_char_classes == NULL, 0))
+	return REG_ESPACE;
+      mbcset->char_classes = new_char_classes;
+      *char_class_alloc = new_char_class_alloc;
+    }
+  mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name);
+#endif /* RE_ENABLE_I18N */
+
+#define BUILD_CHARCLASS_LOOP(ctype_func)	\
+  do {						\
+    if (BE (trans != NULL, 0))			\
+      {						\
+	for (i = 0; i < SBC_MAX; ++i)		\
+  	  if (ctype_func (i))			\
+	    bitset_set (sbcset, trans[i]);	\
+      }						\
+    else					\
+      {						\
+	for (i = 0; i < SBC_MAX; ++i)		\
+  	  if (ctype_func (i))			\
+	    bitset_set (sbcset, i);		\
+      }						\
+  } while (0)
+
+  if (strcmp (name, "alnum") == 0)
+    BUILD_CHARCLASS_LOOP (isalnum);
+  else if (strcmp (name, "cntrl") == 0)
+    BUILD_CHARCLASS_LOOP (iscntrl);
+  else if (strcmp (name, "lower") == 0)
+    BUILD_CHARCLASS_LOOP (islower);
+  else if (strcmp (name, "space") == 0)
+    BUILD_CHARCLASS_LOOP (isspace);
+  else if (strcmp (name, "alpha") == 0)
+    BUILD_CHARCLASS_LOOP (isalpha);
+  else if (strcmp (name, "digit") == 0)
+    BUILD_CHARCLASS_LOOP (isdigit);
+  else if (strcmp (name, "print") == 0)
+    BUILD_CHARCLASS_LOOP (isprint);
+  else if (strcmp (name, "upper") == 0)
+    BUILD_CHARCLASS_LOOP (isupper);
+  else if (strcmp (name, "blank") == 0)
+    BUILD_CHARCLASS_LOOP (isblank);
+  else if (strcmp (name, "graph") == 0)
+    BUILD_CHARCLASS_LOOP (isgraph);
+  else if (strcmp (name, "punct") == 0)
+    BUILD_CHARCLASS_LOOP (ispunct);
+  else if (strcmp (name, "xdigit") == 0)
+    BUILD_CHARCLASS_LOOP (isxdigit);
+  else
+    return REG_ECTYPE;
+
+  return REG_NOERROR;
+}
+
+static bin_tree_t *
+build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans,
+		    const unsigned char *class_name,
+		    const unsigned char *extra, int non_match,
+		    reg_errcode_t *err)
+{
+  re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+  re_charset_t *mbcset;
+  int alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+  reg_errcode_t ret;
+  re_token_t br_token;
+  bin_tree_t *tree;
+
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+#ifdef RE_ENABLE_I18N
+  mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+
+#ifdef RE_ENABLE_I18N
+  if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else /* not RE_ENABLE_I18N */
+  if (BE (sbcset == NULL, 0))
+#endif /* not RE_ENABLE_I18N */
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+
+  if (non_match)
+    {
+#ifdef RE_ENABLE_I18N
+      mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+    }
+
+  /* We don't care the syntax in this case.  */
+  ret = build_charclass (trans, sbcset,
+#ifdef RE_ENABLE_I18N
+			 mbcset, &alloc,
+#endif /* RE_ENABLE_I18N */
+			 class_name, 0);
+
+  if (BE (ret != REG_NOERROR, 0))
+    {
+      re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+      free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+      *err = ret;
+      return NULL;
+    }
+  /* \w match '_' also.  */
+  for (; *extra; extra++)
+    bitset_set (sbcset, *extra);
+
+  /* If it is non-matching list.  */
+  if (non_match)
+    bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+  /* Ensure only single byte characters are set.  */
+  if (dfa->mb_cur_max > 1)
+    bitset_mask (sbcset, dfa->sb_char);
+#endif
+
+  /* Build a tree for simple bracket.  */
+  br_token.type = SIMPLE_BRACKET;
+  br_token.opr.sbcset = sbcset;
+  tree = create_token_tree (dfa, NULL, NULL, &br_token);
+  if (BE (tree == NULL, 0))
+    goto build_word_op_espace;
+
+#ifdef RE_ENABLE_I18N
+  if (dfa->mb_cur_max > 1)
+    {
+      bin_tree_t *mbc_tree;
+      /* Build a tree for complex bracket.  */
+      br_token.type = COMPLEX_BRACKET;
+      br_token.opr.mbcset = mbcset;
+      dfa->has_mb_node = 1;
+      mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (mbc_tree == NULL, 0))
+	goto build_word_op_espace;
+      /* Then join them by ALT node.  */
+      tree = create_tree (dfa, tree, mbc_tree, OP_ALT);
+      if (BE (mbc_tree != NULL, 1))
+	return tree;
+    }
+  else
+    {
+      free_charset (mbcset);
+      return tree;
+    }
+#else /* not RE_ENABLE_I18N */
+  return tree;
+#endif /* not RE_ENABLE_I18N */
+
+ build_word_op_espace:
+  re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+  free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+  *err = REG_ESPACE;
+  return NULL;
+}
+
+/* This is intended for the expressions like "a{1,3}".
+   Fetch a number from `input', and return the number.
+   Return -1, if the number field is empty like "{,1}".
+   Return -2, If an error is occured.  */
+
+static int
+fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax)
+{
+  int num = -1;
+  unsigned char c;
+  while (1)
+    {
+      fetch_token (token, input, syntax);
+      c = token->opr.c;
+      if (BE (token->type == END_OF_RE, 0))
+	return -2;
+      if (token->type == OP_CLOSE_DUP_NUM || c == ',')
+	break;
+      num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2)
+	     ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0'));
+      num = (num > RE_DUP_MAX) ? -2 : num;
+    }
+  return num;
+}
+

+#ifdef RE_ENABLE_I18N
+static void
+free_charset (re_charset_t *cset)
+{
+  re_free (cset->mbchars);
+# ifdef _LIBC
+  re_free (cset->coll_syms);
+  re_free (cset->equiv_classes);
+  re_free (cset->range_starts);
+  re_free (cset->range_ends);
+# endif
+  re_free (cset->char_classes);
+  re_free (cset);
+}
+#endif /* RE_ENABLE_I18N */
+

+/* Functions for binary tree operation.  */
+
+/* Create a tree node.  */
+
+static bin_tree_t *
+create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+	     re_token_type_t type)
+{
+  re_token_t t;
+  t.type = type;
+  return create_token_tree (dfa, left, right, &t);
+}
+
+static bin_tree_t *
+create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+		   const re_token_t *token)
+{
+  bin_tree_t *tree;
+  if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0))
+    {
+      bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1);
+
+      if (storage == NULL)
+	return NULL;
+      storage->next = dfa->str_tree_storage;
+      dfa->str_tree_storage = storage;
+      dfa->str_tree_storage_idx = 0;
+    }
+  tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++];
+
+  tree->parent = NULL;
+  tree->left = left;
+  tree->right = right;
+  tree->token = *token;
+  tree->token.duplicated = 0;
+  tree->token.opt_subexp = 0;
+  tree->first = NULL;
+  tree->next = NULL;
+  tree->node_idx = -1;
+
+  if (left != NULL)
+    left->parent = tree;
+  if (right != NULL)
+    right->parent = tree;
+  return tree;
+}
+
+/* Mark the tree SRC as an optional subexpression.
+   To be called from preorder or postorder.  */
+
+static reg_errcode_t
+mark_opt_subexp (void *extra, bin_tree_t *node)
+{
+  int idx = (int) (long) extra;
+  if (node->token.type == SUBEXP && node->token.opr.idx == idx)
+    node->token.opt_subexp = 1;
+
+  return REG_NOERROR;
+}
+
+/* Free the allocated memory inside NODE. */
+
+static void
+free_token (re_token_t *node)
+{
+#ifdef RE_ENABLE_I18N
+  if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
+    free_charset (node->opr.mbcset);
+  else
+#endif /* RE_ENABLE_I18N */
+    if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
+      re_free (node->opr.sbcset);
+}
+
+/* Worker function for tree walking.  Free the allocated memory inside NODE
+   and its children. */
+
+static reg_errcode_t
+free_tree (void *extra, bin_tree_t *node)
+{
+  free_token (&node->token);
+  return REG_NOERROR;
+}
+
+
+/* Duplicate the node SRC, and return new node.  This is a preorder
+   visit similar to the one implemented by the generic visitor, but
+   we need more infrastructure to maintain two parallel trees --- so,
+   it's easier to duplicate.  */
+
+static bin_tree_t *
+duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa)
+{
+  const bin_tree_t *node;
+  bin_tree_t *dup_root;
+  bin_tree_t **p_new = &dup_root, *dup_node = root->parent;
+
+  for (node = root; ; )
+    {
+      /* Create a new tree and link it back to the current parent.  */
+      *p_new = create_token_tree (dfa, NULL, NULL, &node->token);
+      if (*p_new == NULL)
+	return NULL;
+      (*p_new)->parent = dup_node;
+      (*p_new)->token.duplicated = 1;
+      dup_node = *p_new;
+
+      /* Go to the left node, or up and to the right.  */
+      if (node->left)
+	{
+	  node = node->left;
+	  p_new = &dup_node->left;
+	}
+      else
+	{
+	  const bin_tree_t *prev = NULL;
+	  while (node->right == prev || node->right == NULL)
+	    {
+	      prev = node;
+	      node = node->parent;
+	      dup_node = dup_node->parent;
+	      if (!node)
+	        return dup_root;
+	    }
+	  node = node->right;
+	  p_new = &dup_node->right;
+	}
+    }
+}
diff --git a/regex/regex.c b/regex/regex.c
new file mode 100755
index 0000000..d2d4f28
--- /dev/null
+++ b/regex/regex.c
@@ -0,0 +1,74 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu at yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Make sure noone compiles this code with a C++ compiler.  */
+#ifdef __cplusplus
+# error "This is C code, use a C compiler"
+#endif
+
+#ifdef _LIBC
+/* We have to keep the namespace clean.  */
+# define regfree(preg) __regfree (preg)
+# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
+# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
+# define regerror(errcode, preg, errbuf, errbuf_size) \
+	__regerror(errcode, preg, errbuf, errbuf_size)
+# define re_set_registers(bu, re, nu, st, en) \
+	__re_set_registers (bu, re, nu, st, en)
+# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
+	__re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+# define re_match(bufp, string, size, pos, regs) \
+	__re_match (bufp, string, size, pos, regs)
+# define re_search(bufp, string, size, startpos, range, regs) \
+	__re_search (bufp, string, size, startpos, range, regs)
+# define re_compile_pattern(pattern, length, bufp) \
+	__re_compile_pattern (pattern, length, bufp)
+# define re_set_syntax(syntax) __re_set_syntax (syntax)
+# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
+	__re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
+# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
+
+# include "../locale/localeinfo.h"
+#endif
+
+/* On some systems, limits.h sets RE_DUP_MAX to a lower value than
+   GNU regex allows.  Include it before <regex.h>, which correctly
+   #undefs RE_DUP_MAX and sets it to the right value.  */
+#include <limits.h>
+
+#include <regex.h>
+#include "regex_internal.h"
+
+#include "regex_internal.c"
+#include "regcomp.c"
+#include "regexec.c"
+
+/* Binary backward compatibility.  */
+#if _LIBC
+# include <shlib-compat.h>
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3)
+link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
+int re_max_failures = 2000;
+# endif
+#endif
diff --git a/regex/regex.h b/regex/regex.h
new file mode 100755
index 0000000..c2a9a4c
--- /dev/null
+++ b/regex/regex.h
@@ -0,0 +1,580 @@
+/* Definitions for data structures and routines for the regular
+   expression library.
+   Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006
+   Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _REGEX_H
+#define _REGEX_H 1
+
+#include <sys/types.h>
+
+#ifndef __GNUC__
+# define __DLL_IMPORT__	__declspec(dllimport)
+# define __DLL_EXPORT__	__declspec(dllexport)
+#else
+# define __DLL_IMPORT__	__attribute__((dllimport)) extern
+# define __DLL_EXPORT__	__attribute__((dllexport)) extern
+#endif 
+
+#if (defined __WIN32__) || (defined _WIN32)
+# ifdef BUILD_REGEX_DLL
+#  define REGEX_DLL_IMPEXP	__DLL_EXPORT__
+# elif defined(REGEX_STATIC)
+#  define REGEX_DLL_IMPEXP	 
+# elif defined (USE_REGEX_DLL)
+#  define REGEX_DLL_IMPEXP	__DLL_IMPORT__
+# elif defined (USE_REGEX_STATIC)
+#  define REGEX_DLL_IMPEXP 	 
+# else /* assume USE_REGEX_DLL */
+#  define REGEX_DLL_IMPEXP	__DLL_IMPORT__
+# endif
+#else /* __WIN32__ */
+# define REGEX_DLL_IMPEXP	 
+#endif
+
+/* Allow the use in C++ code.  */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The following two types have to be signed and unsigned integer type
+   wide enough to hold a value of a pointer.  For most ANSI compilers
+   ptrdiff_t and size_t should be likely OK.  Still size of these two
+   types is 2 for Microsoft C.  Ugh... */
+typedef long int s_reg_t;
+typedef unsigned long int active_reg_t;
+
+/* The following bits are used to determine the regexp syntax we
+   recognize.  The set/not-set meanings are chosen so that Emacs syntax
+   remains the value 0.  The bits are given in alphabetical order, and
+   the definitions shifted by one from the previous bit; thus, when we
+   add or remove a bit, only one other definition need change.  */
+typedef unsigned long int reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+   If set, then such a \ quotes the following character.  */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+     literals.
+   If set, then \+ and \? are operators and + and ? are literals.  */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported.  They are:
+     [:alpha:], [:upper:], [:lower:],  [:digit:], [:alnum:], [:xdigit:],
+     [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+   If not set, then character classes are not supported.  */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+     expressions, of course).
+   If this bit is not set, then it depends:
+        ^  is an anchor if it is at the beginning of a regular
+           expression or after an open-group or an alternation operator;
+        $  is an anchor if it is at the end of a regular expression, or
+           before a close-group or an alternation operator.
+
+   This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+   POSIX draft 11.2 says that * etc. in leading positions is undefined.
+   We already implemented a previous draft which made those constructs
+   invalid, though, so we haven't changed the code back.  */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+     regardless of where they are in the pattern.
+   If this bit is not set, then special characters are special only in
+     some contexts; otherwise they are ordinary.  Specifically,
+     * + ? and intervals are only special when not after the beginning,
+     open-group, or alternation operator.  */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+     immediately after an alternation or begin-group operator.  */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+   If not set, then it doesn't.  */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+   If not set, then it does.  */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+   If not set, they do.  */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+     interval, depending on RE_NO_BK_BRACES.
+   If not set, \{, \}, {, and } are literals.  */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+   If not set, they are.  */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+   If not set, newline is literal.  */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+     are literals.
+  If not set, then `\{...\}' defines an interval.  */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+   If not set, \(...\) defines a group, and ( and ) are literals.  */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+   If not set, then \<digit> is a back-reference.  */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+   If not set, then \| is an alternation operator, and | is literal.  */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+     than the starting range point, as in [z-a], is invalid.
+   If not set, then when ending range point collates higher than the
+     starting range point, the range is ignored.  */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+   If not set, then an unmatched ) is invalid.  */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, succeed as soon as we match the whole pattern,
+   without further backtracking.  */
+#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
+
+/* If this bit is set, do not process the GNU regex operators.
+   If not set, then the GNU regex operators are recognized. */
+#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
+
+/* If this bit is set, turn on internal regex debugging.
+   If not set, and debugging was on, turn it off.
+   This only works if regex.c is compiled -DDEBUG.
+   We define this bit always, so that all that's needed to turn on
+   debugging is to recompile regex.c; the calling code can always have
+   this bit set, and it won't affect anything in the normal case. */
+#define RE_DEBUG (RE_NO_GNU_OPS << 1)
+
+/* If this bit is set, a syntactically invalid interval is treated as
+   a string of ordinary characters.  For example, the ERE 'a{1' is
+   treated as 'a\{1'.  */
+#define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1)
+
+/* If this bit is set, then ignore case when matching.
+   If not set, then case is significant.  */
+#define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
+
+/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only
+   for ^, because it is difficult to scan the regex backwards to find
+   whether ^ should be special.  */
+#define RE_CARET_ANCHORS_HERE (RE_ICASE << 1)
+
+/* If this bit is set, then \{ cannot be first in an bre or
+   immediately after an alternation or begin-group operator.  */
+#define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1)
+
+/* If this bit is set, then no_sub will be set to 1 during
+   re_compile_pattern.  */
+#define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+   some interfaces).  When a regexp is compiled, the syntax used is
+   stored in the pattern buffer, so changing this does not affect
+   already-compiled regexps.  */
+REGEX_DLL_IMPEXP reg_syntax_t re_syntax_options;
+

+/* Define combinations of the above bits for the standard possibilities.
+   (The [[[ comments delimit what gets put into the Texinfo file, so
+   don't delete them!)  */
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK							\
+  (RE_BACKSLASH_ESCAPE_IN_LISTS   | RE_DOT_NOT_NULL			\
+   | RE_NO_BK_PARENS              | RE_NO_BK_REFS			\
+   | RE_NO_BK_VBAR                | RE_NO_EMPTY_RANGES			\
+   | RE_DOT_NEWLINE		  | RE_CONTEXT_INDEP_ANCHORS		\
+   | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GNU_AWK						\
+  ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG)	\
+   & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS		\
+       | RE_CONTEXT_INVALID_OPS ))
+
+#define RE_SYNTAX_POSIX_AWK						\
+  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS		\
+   | RE_INTERVALS	    | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GREP							\
+  (RE_BK_PLUS_QM              | RE_CHAR_CLASSES				\
+   | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS				\
+   | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP							\
+  (RE_CHAR_CLASSES        | RE_CONTEXT_INDEP_ANCHORS			\
+   | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE			\
+   | RE_NEWLINE_ALT       | RE_NO_BK_PARENS				\
+   | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP						\
+  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES			\
+   | RE_INVALID_INTERVAL_ORD)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff.  */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax.  */
+#define _RE_SYNTAX_POSIX_COMMON						\
+  (RE_CHAR_CLASSES | RE_DOT_NEWLINE      | RE_DOT_NOT_NULL		\
+   | RE_INTERVALS  | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC						\
+  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+   RE_LIMITED_OPS, i.e., \? \+ \| are not recognized.  Actually, this
+   isn't minimal, since other operators, such as \`, aren't disabled.  */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC					\
+  (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED					\
+  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS			\
+   | RE_CONTEXT_INDEP_OPS   | RE_NO_BK_BRACES				\
+   | RE_NO_BK_PARENS        | RE_NO_BK_VBAR				\
+   | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
+   removed and RE_NO_BK_REFS is added.  */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED				\
+  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS			\
+   | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES				\
+   | RE_NO_BK_PARENS        | RE_NO_BK_REFS				\
+   | RE_NO_BK_VBAR	    | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+

+/* Maximum number of duplicates an interval can allow.  Some systems
+   (erroneously) define this in other header files, but we want our
+   value, so remove any previous define.  */
+#ifdef RE_DUP_MAX
+# undef RE_DUP_MAX
+#endif
+/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows.  */
+#define RE_DUP_MAX (0x7fff)
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp').  */
+
+/* If this bit is set, then use extended regular expression syntax.
+   If not set, then use basic regular expression syntax.  */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+   If not set, then case is significant.  */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+     characters in the string.
+   If not set, then anchors do match at newlines.  */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+   If not set, then returns differ between not matching and errors.  */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec).  */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+     the beginning of the string (presumably because it's not the
+     beginning of a line).
+   If not set, then the beginning-of-line operator does match the
+     beginning of the string.  */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line.  */
+#define REG_NOTEOL (1 << 1)
+
+/* Use PMATCH[0] to delimit the start and end of the search in the
+   buffer.  */
+#define REG_STARTEND (1 << 2)
+
+
+/* If any error codes are removed, changed, or added, update the
+   `re_error_msg' table in regex.c.  */
+typedef enum
+{
+#ifdef _XOPEN_SOURCE
+  REG_ENOSYS = -1,	/* This will never happen for this implementation.  */
+#endif
+
+  REG_NOERROR = 0,	/* Success.  */
+  REG_NOMATCH,		/* Didn't find a match (for regexec).  */
+
+  /* POSIX regcomp return error codes.  (In the order listed in the
+     standard.)  */
+  REG_BADPAT,		/* Invalid pattern.  */
+  REG_ECOLLATE,		/* Inalid collating element.  */
+  REG_ECTYPE,		/* Invalid character class name.  */
+  REG_EESCAPE,		/* Trailing backslash.  */
+  REG_ESUBREG,		/* Invalid back reference.  */
+  REG_EBRACK,		/* Unmatched left bracket.  */
+  REG_EPAREN,		/* Parenthesis imbalance.  */
+  REG_EBRACE,		/* Unmatched \{.  */
+  REG_BADBR,		/* Invalid contents of \{\}.  */
+  REG_ERANGE,		/* Invalid range end.  */
+  REG_ESPACE,		/* Ran out of memory.  */
+  REG_BADRPT,		/* No preceding re for repetition op.  */
+
+  /* Error codes we've added.  */
+  REG_EEND,		/* Premature end.  */
+  REG_ESIZE,		/* Compiled pattern bigger than 2^16 bytes.  */
+  REG_ERPAREN		/* Unmatched ) or \); not returned from regcomp.  */
+} reg_errcode_t;
+

+/* This data structure represents a compiled pattern.  Before calling
+   the pattern compiler, the fields `buffer', `allocated', `fastmap',
+   `translate', and `no_sub' can be set.  After the pattern has been
+   compiled, the `re_nsub' field is available.  All other fields are
+   private to the regex routines.  */
+
+#ifndef RE_TRANSLATE_TYPE
+# define RE_TRANSLATE_TYPE unsigned char *
+#endif
+
+struct re_pattern_buffer
+{
+  /* Space that holds the compiled pattern.  It is declared as
+     `unsigned char *' because its elements are sometimes used as
+     array indexes.  */
+  unsigned char *buffer;
+
+  /* Number of bytes to which `buffer' points.  */
+  unsigned long int allocated;
+
+  /* Number of bytes actually used in `buffer'.  */
+  unsigned long int used;
+
+  /* Syntax setting with which the pattern was compiled.  */
+  reg_syntax_t syntax;
+
+  /* Pointer to a fastmap, if any, otherwise zero.  re_search uses the
+     fastmap, if there is one, to skip over impossible starting points
+     for matches.  */
+  char *fastmap;
+
+  /* Either a translate table to apply to all characters before
+     comparing them, or zero for no translation.  The translation is
+     applied to a pattern when it is compiled and to a string when it
+     is matched.  */
+  RE_TRANSLATE_TYPE translate;
+
+  /* Number of subexpressions found by the compiler.  */
+  size_t re_nsub;
+
+  /* Zero if this pattern cannot match the empty string, one else.
+     Well, in truth it's used only in `re_search_2', to see whether or
+     not we should use the fastmap, so we don't set this absolutely
+     perfectly; see `re_compile_fastmap' (the `duplicate' case).  */
+  unsigned can_be_null : 1;
+
+  /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+     for `max (RE_NREGS, re_nsub + 1)' groups.
+     If REGS_REALLOCATE, reallocate space if necessary.
+     If REGS_FIXED, use what's there.  */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+  unsigned regs_allocated : 2;
+
+  /* Set to zero when `regex_compile' compiles a pattern; set to one
+     by `re_compile_fastmap' if it updates the fastmap.  */
+  unsigned fastmap_accurate : 1;
+
+  /* If set, `re_match_2' does not return information about
+     subexpressions.  */
+  unsigned no_sub : 1;
+
+  /* If set, a beginning-of-line anchor doesn't match at the beginning
+     of the string.  */
+  unsigned not_bol : 1;
+
+  /* Similarly for an end-of-line anchor.  */
+  unsigned not_eol : 1;
+
+  /* If true, an anchor at a newline matches.  */
+  unsigned newline_anchor : 1;
+};
+
+typedef struct re_pattern_buffer regex_t;
+

+/* Type for byte offsets within the string.  POSIX mandates this.  */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in.  See
+   regex.texinfo for a full description of what registers match.  */
+struct re_registers
+{
+  unsigned num_regs;
+  regoff_t *start;
+  regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+   `re_match_2' returns information about at least this many registers
+   the first time a `regs' structure is passed.  */
+#ifndef RE_NREGS
+# define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers.  Aside from the different names than
+   `re_registers', POSIX uses an array of structures, instead of a
+   structure of arrays.  */
+typedef struct
+{
+  regoff_t rm_so;  /* Byte offset from string's start to substring's start.  */
+  regoff_t rm_eo;  /* Byte offset from string's start to substring's end.  */
+} regmatch_t;
+

+/* Declarations for routines.  */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+   You can also simply assign to the `re_syntax_options' variable.  */
+REGEX_DLL_IMPEXP reg_syntax_t re_set_syntax (reg_syntax_t __syntax);
+
+/* Compile the regular expression PATTERN, with length LENGTH
+   and syntax given by the global `re_syntax_options', into the buffer
+   BUFFER.  Return NULL if successful, and an error string if not.  */
+REGEX_DLL_IMPEXP const char *re_compile_pattern (const char *__pattern, size_t __length,
+				       struct re_pattern_buffer *__buffer);
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+   accelerate searches.  Return 0 if successful and -2 if was an
+   internal error.  */
+REGEX_DLL_IMPEXP int re_compile_fastmap (struct re_pattern_buffer *__buffer);
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+   compiled into BUFFER.  Start searching at position START, for RANGE
+   characters.  Return the starting position of the match, -1 for no
+   match, or -2 for an internal error.  Also return register
+   information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
+REGEX_DLL_IMPEXP int re_search (struct re_pattern_buffer *__buffer, const char *__string,
+		      int __length, int __start, int __range,
+		      struct re_registers *__regs);
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+   STRING2.  Also, stop searching at index START + STOP.  */
+REGEX_DLL_IMPEXP int re_search_2 (struct re_pattern_buffer *__buffer,
+			const char *__string1, int __length1,
+			const char *__string2, int __length2, int __start,
+			int __range, struct re_registers *__regs, int __stop);
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+   in BUFFER matched, starting at position START.  */
+REGEX_DLL_IMPEXP int re_match (struct re_pattern_buffer *__buffer, const char *__string,
+		     int __length, int __start, struct re_registers *__regs);
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
+REGEX_DLL_IMPEXP int re_match_2 (struct re_pattern_buffer *__buffer,
+		       const char *__string1, int __length1,
+		       const char *__string2, int __length2, int __start,
+		       struct re_registers *__regs, int __stop);
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using BUFFER and REGS will use this memory
+   for recording register information.  STARTS and ENDS must be
+   allocated with malloc, and must each be at least `NUM_REGS * sizeof
+   (regoff_t)' bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+REGEX_DLL_IMPEXP void re_set_registers (struct re_pattern_buffer *__buffer,
+			      struct re_registers *__regs,
+			      unsigned int __num_regs,
+			      regoff_t *__starts, regoff_t *__ends);
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+# ifndef _CRAY
+/* 4.2 bsd compatibility.  */
+REGEX_DLL_IMPEXP char *re_comp (const char *);
+REGEX_DLL_IMPEXP int re_exec (const char *);
+# endif
+#endif
+
+/* GCC 2.95 and later have "__restrict"; C99 compilers have
+   "restrict", and "configure" may have defined "restrict".  */
+#ifndef __restrict
+# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
+#  if defined restrict || 199901L <= __STDC_VERSION__
+#   define __restrict restrict
+#  else
+#   define __restrict
+#  endif
+# endif
+#endif
+/* gcc 3.1 and up support the [restrict] syntax.  */
+#ifndef __restrict_arr
+# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) \
+     && !defined __GNUG__
+#  define __restrict_arr __restrict
+# else
+#  define __restrict_arr
+# endif
+#endif
+
+/* POSIX compatibility.  */
+REGEX_DLL_IMPEXP int regcomp (regex_t *__restrict __preg,
+		    const char *__restrict __pattern,
+		    int __cflags);
+
+REGEX_DLL_IMPEXP int regexec (const regex_t *__restrict __preg,
+		    const char *__restrict __string, size_t __nmatch,
+		    regmatch_t __pmatch[__restrict_arr],
+		    int __eflags);
+
+REGEX_DLL_IMPEXP size_t regerror (int __errcode, const regex_t *__restrict __preg,
+			char *__restrict __errbuf, size_t __errbuf_size);
+
+REGEX_DLL_IMPEXP void regfree (regex_t *__preg);
+
+
+#ifdef __cplusplus
+}
+#endif	/* C++ */
+
+#endif /* regex.h */
diff --git a/regex/regex_internal.c b/regex/regex_internal.c
new file mode 100755
index 0000000..66154e0
--- /dev/null
+++ b/regex/regex_internal.c
@@ -0,0 +1,1717 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu at yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+static void re_string_construct_common (const char *str, int len,
+					re_string_t *pstr,
+					RE_TRANSLATE_TYPE trans, int icase,
+					const re_dfa_t *dfa) internal_function;
+static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa,
+					  const re_node_set *nodes,
+					  unsigned int hash) internal_function;
+static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa,
+					  const re_node_set *nodes,
+					  unsigned int context,
+					  unsigned int hash) internal_function;
+

+/* Functions for string operation.  */
+
+/* This function allocate the buffers.  It is necessary to call
+   re_string_reconstruct before using the object.  */
+
+static reg_errcode_t
+internal_function
+re_string_allocate (re_string_t *pstr, const char *str, int len, int init_len,
+		    RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa)
+{
+  reg_errcode_t ret;
+  int init_buf_len;
+
+  /* Ensure at least one character fits into the buffers.  */
+  if (init_len < dfa->mb_cur_max)
+    init_len = dfa->mb_cur_max;
+  init_buf_len = (len + 1 < init_len) ? len + 1: init_len;
+  re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+  ret = re_string_realloc_buffers (pstr, init_buf_len);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  pstr->word_char = dfa->word_char;
+  pstr->word_ops_used = dfa->word_ops_used;
+  pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+  pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len;
+  pstr->valid_raw_len = pstr->valid_len;
+  return REG_NOERROR;
+}
+
+/* This function allocate the buffers, and initialize them.  */
+
+static reg_errcode_t
+internal_function
+re_string_construct (re_string_t *pstr, const char *str, int len,
+		     RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa)
+{
+  reg_errcode_t ret;
+  memset (pstr, '\0', sizeof (re_string_t));
+  re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+  if (len > 0)
+    {
+      ret = re_string_realloc_buffers (pstr, len + 1);
+      if (BE (ret != REG_NOERROR, 0))
+	return ret;
+    }
+  pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+
+  if (icase)
+    {
+#ifdef RE_ENABLE_I18N
+      if (dfa->mb_cur_max > 1)
+	{
+	  while (1)
+	    {
+	      ret = build_wcs_upper_buffer (pstr);
+	      if (BE (ret != REG_NOERROR, 0))
+		return ret;
+	      if (pstr->valid_raw_len >= len)
+		break;
+	      if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max)
+		break;
+	      ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+	      if (BE (ret != REG_NOERROR, 0))
+		return ret;
+	    }
+	}
+      else
+#endif /* RE_ENABLE_I18N  */
+	build_upper_buffer (pstr);
+    }
+  else
+    {
+#ifdef RE_ENABLE_I18N
+      if (dfa->mb_cur_max > 1)
+	build_wcs_buffer (pstr);
+      else
+#endif /* RE_ENABLE_I18N  */
+	{
+	  if (trans != NULL)
+	    re_string_translate_buffer (pstr);
+	  else
+	    {
+	      pstr->valid_len = pstr->bufs_len;
+	      pstr->valid_raw_len = pstr->bufs_len;
+	    }
+	}
+    }
+
+  return REG_NOERROR;
+}
+
+/* Helper functions for re_string_allocate, and re_string_construct.  */
+
+static reg_errcode_t
+internal_function
+re_string_realloc_buffers (re_string_t *pstr, int new_buf_len)
+{
+#ifdef RE_ENABLE_I18N
+  if (pstr->mb_cur_max > 1)
+    {
+      wint_t *new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len);
+      if (BE (new_wcs == NULL, 0))
+	return REG_ESPACE;
+      pstr->wcs = new_wcs;
+      if (pstr->offsets != NULL)
+	{
+	  int *new_offsets = re_realloc (pstr->offsets, int, new_buf_len);
+	  if (BE (new_offsets == NULL, 0))
+	    return REG_ESPACE;
+	  pstr->offsets = new_offsets;
+	}
+    }
+#endif /* RE_ENABLE_I18N  */
+  if (pstr->mbs_allocated)
+    {
+      unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char,
+					   new_buf_len);
+      if (BE (new_mbs == NULL, 0))
+	return REG_ESPACE;
+      pstr->mbs = new_mbs;
+    }
+  pstr->bufs_len = new_buf_len;
+  return REG_NOERROR;
+}
+
+
+static void
+internal_function
+re_string_construct_common (const char *str, int len, re_string_t *pstr,
+			    RE_TRANSLATE_TYPE trans, int icase,
+			    const re_dfa_t *dfa)
+{
+  pstr->raw_mbs = (const unsigned char *) str;
+  pstr->len = len;
+  pstr->raw_len = len;
+  pstr->trans = trans;
+  pstr->icase = icase ? 1 : 0;
+  pstr->mbs_allocated = (trans != NULL || icase);
+  pstr->mb_cur_max = dfa->mb_cur_max;
+  pstr->is_utf8 = dfa->is_utf8;
+  pstr->map_notascii = dfa->map_notascii;
+  pstr->stop = pstr->len;
+  pstr->raw_stop = pstr->stop;
+}
+
+#ifdef RE_ENABLE_I18N
+
+/* Build wide character buffer PSTR->WCS.
+   If the byte sequence of the string are:
+     <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3>
+   Then wide character buffer will be:
+     <wc1>   , WEOF    , <wc2>   , WEOF    , <wc3>
+   We use WEOF for padding, they indicate that the position isn't
+   a first byte of a multibyte character.
+
+   Note that this function assumes PSTR->VALID_LEN elements are already
+   built and starts from PSTR->VALID_LEN.  */
+
+static void
+internal_function
+build_wcs_buffer (re_string_t *pstr)
+{
+#ifdef _LIBC
+  unsigned char buf[MB_LEN_MAX];
+  assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+  unsigned char buf[64];
+#endif
+  mbstate_t prev_st;
+  int byte_idx, end_idx, remain_len;
+  size_t mbclen;
+
+  /* Build the buffers from pstr->valid_len to either pstr->len or
+     pstr->bufs_len.  */
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+  for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
+    {
+      wchar_t wc;
+      const char *p;
+
+      remain_len = end_idx - byte_idx;
+      prev_st = pstr->cur_state;
+      /* Apply the translation if we need.  */
+      if (BE (pstr->trans != NULL, 0))
+	{
+	  int i, ch;
+
+	  for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+	    {
+	      ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i];
+	      buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch];
+	    }
+	  p = (const char *) buf;
+	}
+      else
+	p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx;
+      mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+      if (BE (mbclen == (size_t) -2, 0))
+	{
+	  /* The buffer doesn't have enough space, finish to build.  */
+	  pstr->cur_state = prev_st;
+	  break;
+	}
+      else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0))
+	{
+	  /* We treat these cases as a singlebyte character.  */
+	  mbclen = 1;
+	  wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+	  if (BE (pstr->trans != NULL, 0))
+	    wc = pstr->trans[wc];
+	  pstr->cur_state = prev_st;
+	}
+
+      /* Write wide character and padding.  */
+      pstr->wcs[byte_idx++] = wc;
+      /* Write paddings.  */
+      for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+	pstr->wcs[byte_idx++] = WEOF;
+    }
+  pstr->valid_len = byte_idx;
+  pstr->valid_raw_len = byte_idx;
+}
+
+/* Build wide character buffer PSTR->WCS like build_wcs_buffer,
+   but for REG_ICASE.  */
+
+static reg_errcode_t
+internal_function
+build_wcs_upper_buffer (re_string_t *pstr)
+{
+  mbstate_t prev_st;
+  int src_idx, byte_idx, end_idx, remain_len;
+  size_t mbclen;
+#ifdef _LIBC
+  char buf[MB_LEN_MAX];
+  assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+  char buf[64];
+#endif
+
+  byte_idx = pstr->valid_len;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  /* The following optimization assumes that ASCII characters can be
+     mapped to wide characters with a simple cast.  */
+  if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed)
+    {
+      while (byte_idx < end_idx)
+	{
+	  wchar_t wc;
+
+	  if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx])
+	      && mbsinit (&pstr->cur_state))
+	    {
+	      /* In case of a singlebyte character.  */
+	      pstr->mbs[byte_idx]
+		= toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]);
+	      /* The next step uses the assumption that wchar_t is encoded
+		 ASCII-safe: all ASCII values can be converted like this.  */
+	      pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx];
+	      ++byte_idx;
+	      continue;
+	    }
+
+	  remain_len = end_idx - byte_idx;
+	  prev_st = pstr->cur_state;
+	  mbclen = mbrtowc (&wc,
+			    ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
+			     + byte_idx), remain_len, &pstr->cur_state);
+	  if (BE (mbclen + 2 > 2, 1))
+	    {
+	      wchar_t wcu = wc;
+	      if (iswlower (wc))
+		{
+		  size_t mbcdlen;
+
+		  wcu = towupper (wc);
+		  mbcdlen = wcrtomb (buf, wcu, &prev_st);
+		  if (BE (mbclen == mbcdlen, 1))
+		    memcpy (pstr->mbs + byte_idx, buf, mbclen);
+		  else
+		    {
+		      src_idx = byte_idx;
+		      goto offsets_needed;
+		    }
+		}
+	      else
+		memcpy (pstr->mbs + byte_idx,
+			pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen);
+	      pstr->wcs[byte_idx++] = wcu;
+	      /* Write paddings.  */
+	      for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+		pstr->wcs[byte_idx++] = WEOF;
+	    }
+	  else if (mbclen == (size_t) -1 || mbclen == 0)
+	    {
+	      /* It is an invalid character or '\0'.  Just use the byte.  */
+	      int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+	      pstr->mbs[byte_idx] = ch;
+	      /* And also cast it to wide char.  */
+	      pstr->wcs[byte_idx++] = (wchar_t) ch;
+	      if (BE (mbclen == (size_t) -1, 0))
+		pstr->cur_state = prev_st;
+	    }
+	  else
+	    {
+	      /* The buffer doesn't have enough space, finish to build.  */
+	      pstr->cur_state = prev_st;
+	      break;
+	    }
+	}
+      pstr->valid_len = byte_idx;
+      pstr->valid_raw_len = byte_idx;
+      return REG_NOERROR;
+    }
+  else
+    for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;)
+      {
+	wchar_t wc;
+	const char *p;
+      offsets_needed:
+	remain_len = end_idx - byte_idx;
+	prev_st = pstr->cur_state;
+	if (BE (pstr->trans != NULL, 0))
+	  {
+	    int i, ch;
+
+	    for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+	      {
+		ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i];
+		buf[i] = pstr->trans[ch];
+	      }
+	    p = (const char *) buf;
+	  }
+	else
+	  p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx;
+	mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+	if (BE (mbclen + 2 > 2, 1))
+	  {
+	    wchar_t wcu = wc;
+	    if (iswlower (wc))
+	      {
+		size_t mbcdlen;
+
+		wcu = towupper (wc);
+		mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st);
+		if (BE (mbclen == mbcdlen, 1))
+		  memcpy (pstr->mbs + byte_idx, buf, mbclen);
+		else if (mbcdlen != (size_t) -1)
+		  {
+		    size_t i;
+
+		    if (byte_idx + mbcdlen > pstr->bufs_len)
+		      {
+			pstr->cur_state = prev_st;
+			break;
+		      }
+
+		    if (pstr->offsets == NULL)
+		      {
+			pstr->offsets = re_malloc (int, pstr->bufs_len);
+
+			if (pstr->offsets == NULL)
+			  return REG_ESPACE;
+		      }
+		    if (!pstr->offsets_needed)
+		      {
+			for (i = 0; i < (size_t) byte_idx; ++i)
+			  pstr->offsets[i] = i;
+			pstr->offsets_needed = 1;
+		      }
+
+		    memcpy (pstr->mbs + byte_idx, buf, mbcdlen);
+		    pstr->wcs[byte_idx] = wcu;
+		    pstr->offsets[byte_idx] = src_idx;
+		    for (i = 1; i < mbcdlen; ++i)
+		      {
+			pstr->offsets[byte_idx + i]
+			  = src_idx + (i < mbclen ? i : mbclen - 1);
+			pstr->wcs[byte_idx + i] = WEOF;
+		      }
+		    pstr->len += mbcdlen - mbclen;
+		    if (pstr->raw_stop > src_idx)
+		      pstr->stop += mbcdlen - mbclen;
+		    end_idx = (pstr->bufs_len > pstr->len)
+			      ? pstr->len : pstr->bufs_len;
+		    byte_idx += mbcdlen;
+		    src_idx += mbclen;
+		    continue;
+		  }
+                else
+                  memcpy (pstr->mbs + byte_idx, p, mbclen);
+	      }
+	    else
+	      memcpy (pstr->mbs + byte_idx, p, mbclen);
+
+	    if (BE (pstr->offsets_needed != 0, 0))
+	      {
+		size_t i;
+		for (i = 0; i < mbclen; ++i)
+		  pstr->offsets[byte_idx + i] = src_idx + i;
+	      }
+	    src_idx += mbclen;
+
+	    pstr->wcs[byte_idx++] = wcu;
+	    /* Write paddings.  */
+	    for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+	      pstr->wcs[byte_idx++] = WEOF;
+	  }
+	else if (mbclen == (size_t) -1 || mbclen == 0)
+	  {
+	    /* It is an invalid character or '\0'.  Just use the byte.  */
+	    int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx];
+
+	    if (BE (pstr->trans != NULL, 0))
+	      ch = pstr->trans [ch];
+	    pstr->mbs[byte_idx] = ch;
+
+	    if (BE (pstr->offsets_needed != 0, 0))
+	      pstr->offsets[byte_idx] = src_idx;
+	    ++src_idx;
+
+	    /* And also cast it to wide char.  */
+	    pstr->wcs[byte_idx++] = (wchar_t) ch;
+	    if (BE (mbclen == (size_t) -1, 0))
+	      pstr->cur_state = prev_st;
+	  }
+	else
+	  {
+	    /* The buffer doesn't have enough space, finish to build.  */
+	    pstr->cur_state = prev_st;
+	    break;
+	  }
+      }
+  pstr->valid_len = byte_idx;
+  pstr->valid_raw_len = src_idx;
+  return REG_NOERROR;
+}
+
+/* Skip characters until the index becomes greater than NEW_RAW_IDX.
+   Return the index.  */
+
+static int
+internal_function
+re_string_skip_chars (re_string_t *pstr, int new_raw_idx, wint_t *last_wc)
+{
+  mbstate_t prev_st;
+  int rawbuf_idx;
+  size_t mbclen;
+  wchar_t wc = WEOF;
+
+  /* Skip the characters which are not necessary to check.  */
+  for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len;
+       rawbuf_idx < new_raw_idx;)
+    {
+      int remain_len;
+      remain_len = pstr->len - rawbuf_idx;
+      prev_st = pstr->cur_state;
+      mbclen = mbrtowc (&wc, (const char *) pstr->raw_mbs + rawbuf_idx,
+			remain_len, &pstr->cur_state);
+      if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0))
+	{
+	  /* We treat these cases as a single byte character.  */
+	  if (mbclen == 0 || remain_len == 0)
+	    wc = L'\0';
+	  else
+	    wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx);
+	  mbclen = 1;
+	  pstr->cur_state = prev_st;
+	}
+      /* Then proceed the next character.  */
+      rawbuf_idx += mbclen;
+    }
+  *last_wc = (wint_t) wc;
+  return rawbuf_idx;
+}
+#endif /* RE_ENABLE_I18N  */
+
+/* Build the buffer PSTR->MBS, and apply the translation if we need.
+   This function is used in case of REG_ICASE.  */
+
+static void
+internal_function
+build_upper_buffer (re_string_t *pstr)
+{
+  int char_idx, end_idx;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx)
+    {
+      int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx];
+      if (BE (pstr->trans != NULL, 0))
+	ch = pstr->trans[ch];
+      if (islower (ch))
+	pstr->mbs[char_idx] = toupper (ch);
+      else
+	pstr->mbs[char_idx] = ch;
+    }
+  pstr->valid_len = char_idx;
+  pstr->valid_raw_len = char_idx;
+}
+
+/* Apply TRANS to the buffer in PSTR.  */
+
+static void
+internal_function
+re_string_translate_buffer (re_string_t *pstr)
+{
+  int buf_idx, end_idx;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx)
+    {
+      int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx];
+      pstr->mbs[buf_idx] = pstr->trans[ch];
+    }
+
+  pstr->valid_len = buf_idx;
+  pstr->valid_raw_len = buf_idx;
+}
+
+/* This function re-construct the buffers.
+   Concretely, convert to wide character in case of pstr->mb_cur_max > 1,
+   convert to upper case in case of REG_ICASE, apply translation.  */
+
+static reg_errcode_t
+internal_function
+re_string_reconstruct (re_string_t *pstr, int idx, int eflags)
+{
+  int offset = idx - pstr->raw_mbs_idx;
+  if (BE (offset < 0, 0))
+    {
+      /* Reset buffer.  */
+#ifdef RE_ENABLE_I18N
+      if (pstr->mb_cur_max > 1)
+	memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+#endif /* RE_ENABLE_I18N */
+      pstr->len = pstr->raw_len;
+      pstr->stop = pstr->raw_stop;
+      pstr->valid_len = 0;
+      pstr->raw_mbs_idx = 0;
+      pstr->valid_raw_len = 0;
+      pstr->offsets_needed = 0;
+      pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+			   : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
+      if (!pstr->mbs_allocated)
+	pstr->mbs = (unsigned char *) pstr->raw_mbs;
+      offset = idx;
+    }
+
+  if (BE (offset != 0, 1))
+    {
+      /* Should the already checked characters be kept?  */
+      if (BE (offset < pstr->valid_raw_len, 1))
+	{
+	  /* Yes, move them to the front of the buffer.  */
+#ifdef RE_ENABLE_I18N
+	  if (BE (pstr->offsets_needed, 0))
+	    {
+	      int low = 0, high = pstr->valid_len, mid;
+	      do
+		{
+		  mid = (high + low) / 2;
+		  if (pstr->offsets[mid] > offset)
+		    high = mid;
+		  else if (pstr->offsets[mid] < offset)
+		    low = mid + 1;
+		  else
+		    break;
+		}
+	      while (low < high);
+	      if (pstr->offsets[mid] < offset)
+		++mid;
+	      pstr->tip_context = re_string_context_at (pstr, mid - 1,
+							eflags);
+	      /* This can be quite complicated, so handle specially
+		 only the common and easy case where the character with
+		 different length representation of lower and upper
+		 case is present at or after offset.  */
+	      if (pstr->valid_len > offset
+		  && mid == offset && pstr->offsets[mid] == offset)
+		{
+		  memmove (pstr->wcs, pstr->wcs + offset,
+			   (pstr->valid_len - offset) * sizeof (wint_t));
+		  memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset);
+		  pstr->valid_len -= offset;
+		  pstr->valid_raw_len -= offset;
+		  for (low = 0; low < pstr->valid_len; low++)
+		    pstr->offsets[low] = pstr->offsets[low + offset] - offset;
+		}
+	      else
+		{
+		  /* Otherwise, just find out how long the partial multibyte
+		     character at offset is and fill it with WEOF/255.  */
+		  pstr->len = pstr->raw_len - idx + offset;
+		  pstr->stop = pstr->raw_stop - idx + offset;
+		  pstr->offsets_needed = 0;
+		  while (mid > 0 && pstr->offsets[mid - 1] == offset)
+		    --mid;
+		  while (mid < pstr->valid_len)
+		    if (pstr->wcs[mid] != WEOF)
+		      break;
+		    else
+		      ++mid;
+		  if (mid == pstr->valid_len)
+		    pstr->valid_len = 0;
+		  else
+		    {
+		      pstr->valid_len = pstr->offsets[mid] - offset;
+		      if (pstr->valid_len)
+			{
+			  for (low = 0; low < pstr->valid_len; ++low)
+			    pstr->wcs[low] = WEOF;
+			  memset (pstr->mbs, 255, pstr->valid_len);
+			}
+		    }
+		  pstr->valid_raw_len = pstr->valid_len;
+		}
+	    }
+	  else
+#endif
+	    {
+	      pstr->tip_context = re_string_context_at (pstr, offset - 1,
+							eflags);
+#ifdef RE_ENABLE_I18N
+	      if (pstr->mb_cur_max > 1)
+		memmove (pstr->wcs, pstr->wcs + offset,
+			 (pstr->valid_len - offset) * sizeof (wint_t));
+#endif /* RE_ENABLE_I18N */
+	      if (BE (pstr->mbs_allocated, 0))
+		memmove (pstr->mbs, pstr->mbs + offset,
+			 pstr->valid_len - offset);
+	      pstr->valid_len -= offset;
+	      pstr->valid_raw_len -= offset;
+#if DEBUG
+	      assert (pstr->valid_len > 0);
+#endif
+	    }
+	}
+      else
+	{
+	  /* No, skip all characters until IDX.  */
+	  int prev_valid_len = pstr->valid_len;
+
+#ifdef RE_ENABLE_I18N
+	  if (BE (pstr->offsets_needed, 0))
+	    {
+	      pstr->len = pstr->raw_len - idx + offset;
+	      pstr->stop = pstr->raw_stop - idx + offset;
+	      pstr->offsets_needed = 0;
+	    }
+#endif
+	  pstr->valid_len = 0;
+#ifdef RE_ENABLE_I18N
+	  if (pstr->mb_cur_max > 1)
+	    {
+	      int wcs_idx;
+	      wint_t wc = WEOF;
+
+	      if (pstr->is_utf8)
+		{
+		  const unsigned char *raw, *p, *q, *end;
+
+		  /* Special case UTF-8.  Multi-byte chars start with any
+		     byte other than 0x80 - 0xbf.  */
+		  raw = pstr->raw_mbs + pstr->raw_mbs_idx;
+		  end = raw + (offset - pstr->mb_cur_max);
+		  if (end < pstr->raw_mbs)
+		    end = pstr->raw_mbs;
+		  p = raw + offset - 1;
+#ifdef _LIBC
+		  /* We know the wchar_t encoding is UCS4, so for the simple
+		     case, ASCII characters, skip the conversion step.  */
+		  if (isascii (*p) && BE (pstr->trans == NULL, 1))
+		    {
+		      memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+		      /* pstr->valid_len = 0; */
+		      wc = (wchar_t) *p;
+		    }
+		  else
+#endif
+		    for (; p >= end; --p)
+		      if ((*p & 0xc0) != 0x80)
+			{
+			  mbstate_t cur_state;
+			  wchar_t wc2;
+			  int mlen = raw + pstr->len - p;
+			  unsigned char buf[6];
+			  size_t mbclen;
+
+			  q = p;
+			  if (BE (pstr->trans != NULL, 0))
+			    {
+			      int i = mlen < 6 ? mlen : 6;
+			      while (--i >= 0)
+				buf[i] = pstr->trans[p[i]];
+			      q = buf;
+			    }
+			  /* XXX Don't use mbrtowc, we know which conversion
+			     to use (UTF-8 -> UCS4).  */
+			  memset (&cur_state, 0, sizeof (cur_state));
+			  mbclen = mbrtowc (&wc2, (const char *) p, mlen,
+					    &cur_state);
+			  if (raw + offset - p <= mbclen
+			      && mbclen < (size_t) -2)
+			    {
+			      memset (&pstr->cur_state, '\0',
+				      sizeof (mbstate_t));
+			      pstr->valid_len = mbclen - (raw + offset - p);
+			      wc = wc2;
+			    }
+			  break;
+			}
+		}
+
+	      if (wc == WEOF)
+		pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx;
+	      if (wc == WEOF)
+		pstr->tip_context
+		  = re_string_context_at (pstr, prev_valid_len - 1, eflags);
+	      else
+		pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0)
+				      && IS_WIDE_WORD_CHAR (wc))
+				     ? CONTEXT_WORD
+				     : ((IS_WIDE_NEWLINE (wc)
+					 && pstr->newline_anchor)
+					? CONTEXT_NEWLINE : 0));
+	      if (BE (pstr->valid_len, 0))
+		{
+		  for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx)
+		    pstr->wcs[wcs_idx] = WEOF;
+		  if (pstr->mbs_allocated)
+		    memset (pstr->mbs, 255, pstr->valid_len);
+		}
+	      pstr->valid_raw_len = pstr->valid_len;
+	    }
+	  else
+#endif /* RE_ENABLE_I18N */
+	    {
+	      int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1];
+	      pstr->valid_raw_len = 0;
+	      if (pstr->trans)
+		c = pstr->trans[c];
+	      pstr->tip_context = (bitset_contain (pstr->word_char, c)
+				   ? CONTEXT_WORD
+				   : ((IS_NEWLINE (c) && pstr->newline_anchor)
+				      ? CONTEXT_NEWLINE : 0));
+	    }
+	}
+      if (!BE (pstr->mbs_allocated, 0))
+	pstr->mbs += offset;
+    }
+  pstr->raw_mbs_idx = idx;
+  pstr->len -= offset;
+  pstr->stop -= offset;
+
+  /* Then build the buffers.  */
+#ifdef RE_ENABLE_I18N
+  if (pstr->mb_cur_max > 1)
+    {
+      if (pstr->icase)
+	{
+	  reg_errcode_t ret = build_wcs_upper_buffer (pstr);
+	  if (BE (ret != REG_NOERROR, 0))
+	    return ret;
+	}
+      else
+	build_wcs_buffer (pstr);
+    }
+  else
+#endif /* RE_ENABLE_I18N */
+    if (BE (pstr->mbs_allocated, 0))
+      {
+	if (pstr->icase)
+	  build_upper_buffer (pstr);
+	else if (pstr->trans != NULL)
+	  re_string_translate_buffer (pstr);
+      }
+    else
+      pstr->valid_len = pstr->len;
+
+  pstr->cur_idx = 0;
+  return REG_NOERROR;
+}
+
+static unsigned char
+internal_function __attribute ((pure))
+re_string_peek_byte_case (const re_string_t *pstr, int idx)
+{
+  int ch, off;
+
+  /* Handle the common (easiest) cases first.  */
+  if (BE (!pstr->mbs_allocated, 1))
+    return re_string_peek_byte (pstr, idx);
+
+#ifdef RE_ENABLE_I18N
+  if (pstr->mb_cur_max > 1
+      && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx))
+    return re_string_peek_byte (pstr, idx);
+#endif
+
+  off = pstr->cur_idx + idx;
+#ifdef RE_ENABLE_I18N
+  if (pstr->offsets_needed)
+    off = pstr->offsets[off];
+#endif
+
+  ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+#ifdef RE_ENABLE_I18N
+  /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I
+     this function returns CAPITAL LETTER I instead of first byte of
+     DOTLESS SMALL LETTER I.  The latter would confuse the parser,
+     since peek_byte_case doesn't advance cur_idx in any way.  */
+  if (pstr->offsets_needed && !isascii (ch))
+    return re_string_peek_byte (pstr, idx);
+#endif
+
+  return ch;
+}
+
+static unsigned char
+internal_function __attribute ((pure))
+re_string_fetch_byte_case (re_string_t *pstr)
+{
+  if (BE (!pstr->mbs_allocated, 1))
+    return re_string_fetch_byte (pstr);
+
+#ifdef RE_ENABLE_I18N
+  if (pstr->offsets_needed)
+    {
+      int off, ch;
+
+      /* For tr_TR.UTF-8 [[:islower:]] there is
+	 [[: CAPITAL LETTER I WITH DOT lower:]] in mbs.  Skip
+	 in that case the whole multi-byte character and return
+	 the original letter.  On the other side, with
+	 [[: DOTLESS SMALL LETTER I return [[:I, as doing
+	 anything else would complicate things too much.  */
+
+      if (!re_string_first_byte (pstr, pstr->cur_idx))
+	return re_string_fetch_byte (pstr);
+
+      off = pstr->offsets[pstr->cur_idx];
+      ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+      if (! isascii (ch))
+	return re_string_fetch_byte (pstr);
+
+      re_string_skip_bytes (pstr,
+			    re_string_char_size_at (pstr, pstr->cur_idx));
+      return ch;
+    }
+#endif
+
+  return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++];
+}
+
+static void
+internal_function
+re_string_destruct (re_string_t *pstr)
+{
+#ifdef RE_ENABLE_I18N
+  re_free (pstr->wcs);
+  re_free (pstr->offsets);
+#endif /* RE_ENABLE_I18N  */
+  if (pstr->mbs_allocated)
+    re_free (pstr->mbs);
+}
+
+/* Return the context at IDX in INPUT.  */
+
+static unsigned int
+internal_function
+re_string_context_at (const re_string_t *input, int idx, int eflags)
+{
+  int c;
+  if (BE (idx < 0, 0))
+    /* In this case, we use the value stored in input->tip_context,
+       since we can't know the character in input->mbs[-1] here.  */
+    return input->tip_context;
+  if (BE (idx == input->len, 0))
+    return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF
+	    : CONTEXT_NEWLINE | CONTEXT_ENDBUF);
+#ifdef RE_ENABLE_I18N
+  if (input->mb_cur_max > 1)
+    {
+      wint_t wc;
+      int wc_idx = idx;
+      while(input->wcs[wc_idx] == WEOF)
+	{
+#ifdef DEBUG
+	  /* It must not happen.  */
+	  assert (wc_idx >= 0);
+#endif
+	  --wc_idx;
+	  if (wc_idx < 0)
+	    return input->tip_context;
+	}
+      wc = input->wcs[wc_idx];
+      if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc))
+	return CONTEXT_WORD;
+      return (IS_WIDE_NEWLINE (wc) && input->newline_anchor
+	      ? CONTEXT_NEWLINE : 0);
+    }
+  else
+#endif
+    {
+      c = re_string_byte_at (input, idx);
+      if (bitset_contain (input->word_char, c))
+	return CONTEXT_WORD;
+      return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0;
+    }
+}
+

+/* Functions for set operation.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_alloc (re_node_set *set, int size)
+{
+  set->alloc = size;
+  set->nelem = 0;
+  set->elems = re_malloc (int, size);
+  if (BE (set->elems == NULL, 0))
+    return REG_ESPACE;
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_1 (re_node_set *set, int elem)
+{
+  set->alloc = 1;
+  set->nelem = 1;
+  set->elems = re_malloc (int, 1);
+  if (BE (set->elems == NULL, 0))
+    {
+      set->alloc = set->nelem = 0;
+      return REG_ESPACE;
+    }
+  set->elems[0] = elem;
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_2 (re_node_set *set, int elem1, int elem2)
+{
+  set->alloc = 2;
+  set->elems = re_malloc (int, 2);
+  if (BE (set->elems == NULL, 0))
+    return REG_ESPACE;
+  if (elem1 == elem2)
+    {
+      set->nelem = 1;
+      set->elems[0] = elem1;
+    }
+  else
+    {
+      set->nelem = 2;
+      if (elem1 < elem2)
+	{
+	  set->elems[0] = elem1;
+	  set->elems[1] = elem2;
+	}
+      else
+	{
+	  set->elems[0] = elem2;
+	  set->elems[1] = elem1;
+	}
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_copy (re_node_set *dest, const re_node_set *src)
+{
+  dest->nelem = src->nelem;
+  if (src->nelem > 0)
+    {
+      dest->alloc = dest->nelem;
+      dest->elems = re_malloc (int, dest->alloc);
+      if (BE (dest->elems == NULL, 0))
+	{
+	  dest->alloc = dest->nelem = 0;
+	  return REG_ESPACE;
+	}
+      memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+    }
+  else
+    re_node_set_init_empty (dest);
+  return REG_NOERROR;
+}
+
+/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.
+   Note: We assume dest->elems is NULL, when dest->alloc is 0.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1,
+			   const re_node_set *src2)
+{
+  int i1, i2, is, id, delta, sbase;
+  if (src1->nelem == 0 || src2->nelem == 0)
+    return REG_NOERROR;
+
+  /* We need dest->nelem + 2 * elems_in_intersection; this is a
+     conservative estimate.  */
+  if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
+    {
+      int new_alloc = src1->nelem + src2->nelem + dest->alloc;
+      int *new_elems = re_realloc (dest->elems, int, new_alloc);
+      if (BE (new_elems == NULL, 0))
+        return REG_ESPACE;
+      dest->elems = new_elems;
+      dest->alloc = new_alloc;
+    }
+
+  /* Find the items in the intersection of SRC1 and SRC2, and copy
+     into the top of DEST those that are not already in DEST itself.  */
+  sbase = dest->nelem + src1->nelem + src2->nelem;
+  i1 = src1->nelem - 1;
+  i2 = src2->nelem - 1;
+  id = dest->nelem - 1;
+  for (;;)
+    {
+      if (src1->elems[i1] == src2->elems[i2])
+	{
+	  /* Try to find the item in DEST.  Maybe we could binary search?  */
+	  while (id >= 0 && dest->elems[id] > src1->elems[i1])
+	    --id;
+
+          if (id < 0 || dest->elems[id] != src1->elems[i1])
+            dest->elems[--sbase] = src1->elems[i1];
+
+	  if (--i1 < 0 || --i2 < 0)
+	    break;
+	}
+
+      /* Lower the highest of the two items.  */
+      else if (src1->elems[i1] < src2->elems[i2])
+	{
+	  if (--i2 < 0)
+	    break;
+	}
+      else
+	{
+	  if (--i1 < 0)
+	    break;
+	}
+    }
+
+  id = dest->nelem - 1;
+  is = dest->nelem + src1->nelem + src2->nelem - 1;
+  delta = is - sbase + 1;
+
+  /* Now copy.  When DELTA becomes zero, the remaining
+     DEST elements are already in place; this is more or
+     less the same loop that is in re_node_set_merge.  */
+  dest->nelem += delta;
+  if (delta > 0 && id >= 0)
+    for (;;)
+      {
+        if (dest->elems[is] > dest->elems[id])
+          {
+            /* Copy from the top.  */
+            dest->elems[id + delta--] = dest->elems[is--];
+            if (delta == 0)
+              break;
+          }
+        else
+          {
+            /* Slide from the bottom.  */
+            dest->elems[id + delta] = dest->elems[id];
+            if (--id < 0)
+              break;
+          }
+      }
+
+  /* Copy remaining SRC elements.  */
+  memcpy (dest->elems, dest->elems + sbase, delta * sizeof (int));
+
+  return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets SRC1 and SRC2. And store it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_init_union (re_node_set *dest, const re_node_set *src1,
+			const re_node_set *src2)
+{
+  int i1, i2, id;
+  if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
+    {
+      dest->alloc = src1->nelem + src2->nelem;
+      dest->elems = re_malloc (int, dest->alloc);
+      if (BE (dest->elems == NULL, 0))
+	return REG_ESPACE;
+    }
+  else
+    {
+      if (src1 != NULL && src1->nelem > 0)
+	return re_node_set_init_copy (dest, src1);
+      else if (src2 != NULL && src2->nelem > 0)
+	return re_node_set_init_copy (dest, src2);
+      else
+	re_node_set_init_empty (dest);
+      return REG_NOERROR;
+    }
+  for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+    {
+      if (src1->elems[i1] > src2->elems[i2])
+	{
+	  dest->elems[id++] = src2->elems[i2++];
+	  continue;
+	}
+      if (src1->elems[i1] == src2->elems[i2])
+	++i2;
+      dest->elems[id++] = src1->elems[i1++];
+    }
+  if (i1 < src1->nelem)
+    {
+      memcpy (dest->elems + id, src1->elems + i1,
+	     (src1->nelem - i1) * sizeof (int));
+      id += src1->nelem - i1;
+    }
+  else if (i2 < src2->nelem)
+    {
+      memcpy (dest->elems + id, src2->elems + i2,
+	     (src2->nelem - i2) * sizeof (int));
+      id += src2->nelem - i2;
+    }
+  dest->nelem = id;
+  return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets DEST and SRC. And store it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.  */
+
+static reg_errcode_t
+internal_function
+re_node_set_merge (re_node_set *dest, const re_node_set *src)
+{
+  int is, id, sbase, delta;
+  if (src == NULL || src->nelem == 0)
+    return REG_NOERROR;
+  if (dest->alloc < 2 * src->nelem + dest->nelem)
+    {
+      int new_alloc = 2 * (src->nelem + dest->alloc);
+      int *new_buffer = re_realloc (dest->elems, int, new_alloc);
+      if (BE (new_buffer == NULL, 0))
+	return REG_ESPACE;
+      dest->elems = new_buffer;
+      dest->alloc = new_alloc;
+    }
+
+  if (BE (dest->nelem == 0, 0))
+    {
+      dest->nelem = src->nelem;
+      memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+      return REG_NOERROR;
+    }
+
+  /* Copy into the top of DEST the items of SRC that are not
+     found in DEST.  Maybe we could binary search in DEST?  */
+  for (sbase = dest->nelem + 2 * src->nelem,
+       is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; )
+    {
+      if (dest->elems[id] == src->elems[is])
+        is--, id--;
+      else if (dest->elems[id] < src->elems[is])
+        dest->elems[--sbase] = src->elems[is--];
+      else /* if (dest->elems[id] > src->elems[is]) */
+        --id;
+    }
+
+  if (is >= 0)
+    {
+      /* If DEST is exhausted, the remaining items of SRC must be unique.  */
+      sbase -= is + 1;
+      memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (int));
+    }
+
+  id = dest->nelem - 1;
+  is = dest->nelem + 2 * src->nelem - 1;
+  delta = is - sbase + 1;
+  if (delta == 0)
+    return REG_NOERROR;
+
+  /* Now copy.  When DELTA becomes zero, the remaining
+     DEST elements are already in place.  */
+  dest->nelem += delta;
+  for (;;)
+    {
+      if (dest->elems[is] > dest->elems[id])
+        {
+	  /* Copy from the top.  */
+          dest->elems[id + delta--] = dest->elems[is--];
+	  if (delta == 0)
+	    break;
+	}
+      else
+        {
+          /* Slide from the bottom.  */
+          dest->elems[id + delta] = dest->elems[id];
+	  if (--id < 0)
+	    {
+	      /* Copy remaining SRC elements.  */
+	      memcpy (dest->elems, dest->elems + sbase,
+	              delta * sizeof (int));
+	      break;
+	    }
+	}
+    }
+
+  return REG_NOERROR;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+   SET should not already have ELEM.
+   return -1 if an error is occured, return 1 otherwise.  */
+
+static int
+internal_function
+re_node_set_insert (re_node_set *set, int elem)
+{
+  int idx;
+  /* In case the set is empty.  */
+  if (set->alloc == 0)
+    {
+      if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1))
+	return 1;
+      else
+	return -1;
+    }
+
+  if (BE (set->nelem, 0) == 0)
+    {
+      /* We already guaranteed above that set->alloc != 0.  */
+      set->elems[0] = elem;
+      ++set->nelem;
+      return 1;
+    }
+
+  /* Realloc if we need.  */
+  if (set->alloc == set->nelem)
+    {
+      int *new_elems;
+      set->alloc = set->alloc * 2;
+      new_elems = re_realloc (set->elems, int, set->alloc);
+      if (BE (new_elems == NULL, 0))
+	return -1;
+      set->elems = new_elems;
+    }
+
+  /* Move the elements which follows the new element.  Test the
+     first element separately to skip a check in the inner loop.  */
+  if (elem < set->elems[0])
+    {
+      idx = 0;
+      for (idx = set->nelem; idx > 0; idx--)
+        set->elems[idx] = set->elems[idx - 1];
+    }
+  else
+    {
+      for (idx = set->nelem; set->elems[idx - 1] > elem; idx--)
+        set->elems[idx] = set->elems[idx - 1];
+    }
+
+  /* Insert the new element.  */
+  set->elems[idx] = elem;
+  ++set->nelem;
+  return 1;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+   SET should not already have any element greater than or equal to ELEM.
+   Return -1 if an error is occured, return 1 otherwise.  */
+
+static int
+internal_function
+re_node_set_insert_last (re_node_set *set, int elem)
+{
+  /* Realloc if we need.  */
+  if (set->alloc == set->nelem)
+    {
+      int *new_elems;
+      set->alloc = (set->alloc + 1) * 2;
+      new_elems = re_realloc (set->elems, int, set->alloc);
+      if (BE (new_elems == NULL, 0))
+	return -1;
+      set->elems = new_elems;
+    }
+
+  /* Insert the new element.  */
+  set->elems[set->nelem++] = elem;
+  return 1;
+}
+
+/* Compare two node sets SET1 and SET2.
+   return 1 if SET1 and SET2 are equivalent, return 0 otherwise.  */
+
+static int
+internal_function __attribute ((pure))
+re_node_set_compare (const re_node_set *set1, const re_node_set *set2)
+{
+  int i;
+  if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
+    return 0;
+  for (i = set1->nelem ; --i >= 0 ; )
+    if (set1->elems[i] != set2->elems[i])
+      return 0;
+  return 1;
+}
+
+/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise.  */
+
+static int
+internal_function __attribute ((pure))
+re_node_set_contains (const re_node_set *set, int elem)
+{
+  unsigned int idx, right, mid;
+  if (set->nelem <= 0)
+    return 0;
+
+  /* Binary search the element.  */
+  idx = 0;
+  right = set->nelem - 1;
+  while (idx < right)
+    {
+      mid = (idx + right) / 2;
+      if (set->elems[mid] < elem)
+	idx = mid + 1;
+      else
+	right = mid;
+    }
+  return set->elems[idx] == elem ? idx + 1 : 0;
+}
+
+static void
+internal_function
+re_node_set_remove_at (re_node_set *set, int idx)
+{
+  if (idx < 0 || idx >= set->nelem)
+    return;
+  --set->nelem;
+  for (; idx < set->nelem; idx++)
+    set->elems[idx] = set->elems[idx + 1];
+}
+

+
+/* Add the token TOKEN to dfa->nodes, and return the index of the token.
+   Or return -1, if an error will be occured.  */
+
+static int
+internal_function
+re_dfa_add_node (re_dfa_t *dfa, re_token_t token)
+{
+  int type = token.type;
+  if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0))
+    {
+      size_t new_nodes_alloc = dfa->nodes_alloc * 2;
+      int *new_nexts, *new_indices;
+      re_node_set *new_edests, *new_eclosures;
+      re_token_t *new_nodes;
+
+      /* Avoid overflows.  */
+      if (BE (new_nodes_alloc < dfa->nodes_alloc, 0))
+	return -1;
+
+      new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc);
+      if (BE (new_nodes == NULL, 0))
+	return -1;
+      dfa->nodes = new_nodes;
+      new_nexts = re_realloc (dfa->nexts, int, new_nodes_alloc);
+      new_indices = re_realloc (dfa->org_indices, int, new_nodes_alloc);
+      new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc);
+      new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc);
+      if (BE (new_nexts == NULL || new_indices == NULL
+	      || new_edests == NULL || new_eclosures == NULL, 0))
+	return -1;
+      dfa->nexts = new_nexts;
+      dfa->org_indices = new_indices;
+      dfa->edests = new_edests;
+      dfa->eclosures = new_eclosures;
+      dfa->nodes_alloc = new_nodes_alloc;
+    }
+  dfa->nodes[dfa->nodes_len] = token;
+  dfa->nodes[dfa->nodes_len].constraint = 0;
+#ifdef RE_ENABLE_I18N
+  dfa->nodes[dfa->nodes_len].accept_mb =
+    (type == OP_PERIOD && dfa->mb_cur_max > 1) || type == COMPLEX_BRACKET;
+#endif
+  dfa->nexts[dfa->nodes_len] = -1;
+  re_node_set_init_empty (dfa->edests + dfa->nodes_len);
+  re_node_set_init_empty (dfa->eclosures + dfa->nodes_len);
+  return dfa->nodes_len++;
+}
+
+static inline unsigned int
+internal_function
+calc_state_hash (const re_node_set *nodes, unsigned int context)
+{
+  unsigned int hash = nodes->nelem + context;
+  int i;
+  for (i = 0 ; i < nodes->nelem ; i++)
+    hash += nodes->elems[i];
+  return hash;
+}
+
+/* Search for the state whose node_set is equivalent to NODES.
+   Return the pointer to the state, if we found it in the DFA.
+   Otherwise create the new one and return it.  In case of an error
+   return NULL and set the error code in ERR.
+   Note: - We assume NULL as the invalid state, then it is possible that
+	   return value is NULL and ERR is REG_NOERROR.
+	 - We never return non-NULL value in case of any errors, it is for
+	   optimization.  */
+
+static re_dfastate_t *
+internal_function
+re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa,
+		  const re_node_set *nodes)
+{
+  unsigned int hash;
+  re_dfastate_t *new_state;
+  struct re_state_table_entry *spot;
+  int i;
+  if (BE (nodes->nelem == 0, 0))
+    {
+      *err = REG_NOERROR;
+      return NULL;
+    }
+  hash = calc_state_hash (nodes, 0);
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+  for (i = 0 ; i < spot->num ; i++)
+    {
+      re_dfastate_t *state = spot->array[i];
+      if (hash != state->hash)
+	continue;
+      if (re_node_set_compare (&state->nodes, nodes))
+	return state;
+    }
+
+  /* There are no appropriate state in the dfa, create the new one.  */
+  new_state = create_ci_newstate (dfa, nodes, hash);
+  if (BE (new_state == NULL, 0))
+    *err = REG_ESPACE;
+
+  return new_state;
+}
+
+/* Search for the state whose node_set is equivalent to NODES and
+   whose context is equivalent to CONTEXT.
+   Return the pointer to the state, if we found it in the DFA.
+   Otherwise create the new one and return it.  In case of an error
+   return NULL and set the error code in ERR.
+   Note: - We assume NULL as the invalid state, then it is possible that
+	   return value is NULL and ERR is REG_NOERROR.
+	 - We never return non-NULL value in case of any errors, it is for
+	   optimization.  */
+
+static re_dfastate_t *
+internal_function
+re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa,
+			  const re_node_set *nodes, unsigned int context)
+{
+  unsigned int hash;
+  re_dfastate_t *new_state;
+  struct re_state_table_entry *spot;
+  int i;
+  if (nodes->nelem == 0)
+    {
+      *err = REG_NOERROR;
+      return NULL;
+    }
+  hash = calc_state_hash (nodes, context);
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+  for (i = 0 ; i < spot->num ; i++)
+    {
+      re_dfastate_t *state = spot->array[i];
+      if (state->hash == hash
+	  && state->context == context
+	  && re_node_set_compare (state->entrance_nodes, nodes))
+	return state;
+    }
+  /* There are no appropriate state in `dfa', create the new one.  */
+  new_state = create_cd_newstate (dfa, nodes, context, hash);
+  if (BE (new_state == NULL, 0))
+    *err = REG_ESPACE;
+
+  return new_state;
+}
+
+/* Finish initialization of the new state NEWSTATE, and using its hash value
+   HASH put in the appropriate bucket of DFA's state table.  Return value
+   indicates the error code if failed.  */
+
+static reg_errcode_t
+register_state (const re_dfa_t *dfa, re_dfastate_t *newstate,
+		unsigned int hash)
+{
+  struct re_state_table_entry *spot;
+  reg_errcode_t err;
+  int i;
+
+  newstate->hash = hash;
+  err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem);
+  if (BE (err != REG_NOERROR, 0))
+    return REG_ESPACE;
+  for (i = 0; i < newstate->nodes.nelem; i++)
+    {
+      int elem = newstate->nodes.elems[i];
+      if (!IS_EPSILON_NODE (dfa->nodes[elem].type))
+        re_node_set_insert_last (&newstate->non_eps_nodes, elem);
+    }
+
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+  if (BE (spot->alloc <= spot->num, 0))
+    {
+      int new_alloc = 2 * spot->num + 2;
+      re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *,
+					      new_alloc);
+      if (BE (new_array == NULL, 0))
+	return REG_ESPACE;
+      spot->array = new_array;
+      spot->alloc = new_alloc;
+    }
+  spot->array[spot->num++] = newstate;
+  return REG_NOERROR;
+}
+
+static void
+free_state (re_dfastate_t *state)
+{
+  re_node_set_free (&state->non_eps_nodes);
+  re_node_set_free (&state->inveclosure);
+  if (state->entrance_nodes != &state->nodes)
+    {
+      re_node_set_free (state->entrance_nodes);
+      re_free (state->entrance_nodes);
+    }
+  re_node_set_free (&state->nodes);
+  re_free (state->word_trtable);
+  re_free (state->trtable);
+  re_free (state);
+}
+
+/* Create the new state which is independ of contexts.
+   Return the new state if succeeded, otherwise return NULL.  */
+
+static re_dfastate_t *
+internal_function
+create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+		    unsigned int hash)
+{
+  int i;
+  reg_errcode_t err;
+  re_dfastate_t *newstate;
+
+  newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+  if (BE (newstate == NULL, 0))
+    return NULL;
+  err = re_node_set_init_copy (&newstate->nodes, nodes);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      re_free (newstate);
+      return NULL;
+    }
+
+  newstate->entrance_nodes = &newstate->nodes;
+  for (i = 0 ; i < nodes->nelem ; i++)
+    {
+      re_token_t *node = dfa->nodes + nodes->elems[i];
+      re_token_type_t type = node->type;
+      if (type == CHARACTER && !node->constraint)
+	continue;
+#ifdef RE_ENABLE_I18N
+      newstate->accept_mb |= node->accept_mb;
+#endif /* RE_ENABLE_I18N */
+
+      /* If the state has the halt node, the state is a halt state.  */
+      if (type == END_OF_RE)
+	newstate->halt = 1;
+      else if (type == OP_BACK_REF)
+	newstate->has_backref = 1;
+      else if (type == ANCHOR || node->constraint)
+	newstate->has_constraint = 1;
+    }
+  err = register_state (dfa, newstate, hash);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_state (newstate);
+      newstate = NULL;
+    }
+  return newstate;
+}
+
+/* Create the new state which is depend on the context CONTEXT.
+   Return the new state if succeeded, otherwise return NULL.  */
+
+static re_dfastate_t *
+internal_function
+create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+		    unsigned int context, unsigned int hash)
+{
+  int i, nctx_nodes = 0;
+  reg_errcode_t err;
+  re_dfastate_t *newstate;
+
+  newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+  if (BE (newstate == NULL, 0))
+    return NULL;
+  err = re_node_set_init_copy (&newstate->nodes, nodes);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      re_free (newstate);
+      return NULL;
+    }
+
+  newstate->context = context;
+  newstate->entrance_nodes = &newstate->nodes;
+
+  for (i = 0 ; i < nodes->nelem ; i++)
+    {
+      unsigned int constraint = 0;
+      re_token_t *node = dfa->nodes + nodes->elems[i];
+      re_token_type_t type = node->type;
+      if (node->constraint)
+	constraint = node->constraint;
+
+      if (type == CHARACTER && !constraint)
+	continue;
+#ifdef RE_ENABLE_I18N
+      newstate->accept_mb |= node->accept_mb;
+#endif /* RE_ENABLE_I18N */
+
+      /* If the state has the halt node, the state is a halt state.  */
+      if (type == END_OF_RE)
+	newstate->halt = 1;
+      else if (type == OP_BACK_REF)
+	newstate->has_backref = 1;
+      else if (type == ANCHOR)
+	constraint = node->opr.ctx_type;
+
+      if (constraint)
+	{
+	  if (newstate->entrance_nodes == &newstate->nodes)
+	    {
+	      newstate->entrance_nodes = re_malloc (re_node_set, 1);
+	      if (BE (newstate->entrance_nodes == NULL, 0))
+		{
+		  free_state (newstate);
+		  return NULL;
+		}
+	      re_node_set_init_copy (newstate->entrance_nodes, nodes);
+	      nctx_nodes = 0;
+	      newstate->has_constraint = 1;
+	    }
+
+	  if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context))
+	    {
+	      re_node_set_remove_at (&newstate->nodes, i - nctx_nodes);
+	      ++nctx_nodes;
+	    }
+	}
+    }
+  err = register_state (dfa, newstate, hash);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_state (newstate);
+      newstate = NULL;
+    }
+  return  newstate;
+}
diff --git a/regex/regex_internal.h b/regex/regex_internal.h
new file mode 100755
index 0000000..87fa3fc
--- /dev/null
+++ b/regex/regex_internal.h
@@ -0,0 +1,769 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu at yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _REGEX_INTERNAL_H
+#define _REGEX_INTERNAL_H 1
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC
+# include <langinfo.h>
+#endif
+#if defined HAVE_LOCALE_H || defined _LIBC
+# include <locale.h>
+#endif
+#if defined HAVE_WCHAR_H || defined _LIBC
+# include <wchar.h>
+#endif /* HAVE_WCHAR_H || _LIBC */
+#if defined HAVE_WCTYPE_H || defined _LIBC
+# include <wctype.h>
+#endif /* HAVE_WCTYPE_H || _LIBC */
+#if defined HAVE_STDBOOL_H || defined _LIBC
+# include <stdbool.h>
+#endif /* HAVE_STDBOOL_H || _LIBC */
+#if defined HAVE_STDINT_H || defined _LIBC
+# include <stdint.h>
+#endif /* HAVE_STDINT_H || _LIBC */
+#if defined _LIBC
+# include <bits/libc-lock.h>
+#else
+# define __libc_lock_define(CLASS,NAME)
+# define __libc_lock_init(NAME) do { } while (0)
+# define __libc_lock_lock(NAME) do { } while (0)
+# define __libc_lock_unlock(NAME) do { } while (0)
+#endif
+
+/* In case that the system doesn't have isblank().  */
+#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank
+# define isblank(ch) ((ch) == ' ' || (ch) == '\t')
+#endif
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+#  define _RE_DEFINE_LOCALE_FUNCTIONS 1
+#   include <locale/localeinfo.h>
+#   include <locale/elem-hash.h>
+#   include <locale/coll-lookup.h>
+# endif
+#endif
+
+/* This is for other GNU distributions with internationalized messages.  */
+#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+#  undef gettext
+#  define gettext(msgid) \
+  INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+   strings.  */
+# define gettext_noop(String) String
+#endif
+
+/* For loser systems without the definition.  */
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+#if (defined MB_CUR_MAX && HAVE_LOCALE_H && HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_WCRTOMB && HAVE_MBRTOWC && HAVE_WCSCOLL) || _LIBC
+# define RE_ENABLE_I18N
+#endif
+
+#if __GNUC__ >= 3
+# define BE(expr, val) __builtin_expect (expr, val)
+#else
+# define BE(expr, val) (expr)
+# define inline
+#endif
+
+/* Number of single byte character.  */
+#define SBC_MAX 256
+
+#define COLL_ELEM_LEN_MAX 8
+
+/* The character which represents newline.  */
+#define NEWLINE_CHAR '\n'
+#define WIDE_NEWLINE_CHAR L'\n'
+
+/* Rename to standard API for using out of glibc.  */
+#ifndef _LIBC
+# define __wctype wctype
+# define __iswctype iswctype
+# define __btowc btowc
+# define __mempcpy mempcpy
+# define __wcrtomb wcrtomb
+# define __regfree regfree
+# define attribute_hidden
+#endif /* not _LIBC */
+
+#ifdef __GNUC__
+# define __attribute(arg) __attribute__ (arg)
+#else
+# define __attribute(arg)
+#endif
+
+extern const char __re_error_msgid[] attribute_hidden;
+extern const size_t __re_error_msgid_idx[] attribute_hidden;
+
+/* An integer used to represent a set of bits.  It must be unsigned,
+   and must be at least as wide as unsigned int.  */
+typedef unsigned long int bitset_word_t;
+/* All bits set in a bitset_word_t.  */
+#define BITSET_WORD_MAX ULONG_MAX
+/* Number of bits in a bitset_word_t.  */
+#define BITSET_WORD_BITS (sizeof (bitset_word_t) * CHAR_BIT)
+/* Number of bitset_word_t in a bit_set.  */
+#define BITSET_WORDS (SBC_MAX / BITSET_WORD_BITS)
+typedef bitset_word_t bitset_t[BITSET_WORDS];
+typedef bitset_word_t *re_bitset_ptr_t;
+typedef const bitset_word_t *re_const_bitset_ptr_t;
+
+#define bitset_set(set,i) \
+  (set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS)
+#define bitset_clear(set,i) \
+  (set[i / BITSET_WORD_BITS] &= ~((bitset_word_t) 1 << i % BITSET_WORD_BITS))
+#define bitset_contain(set,i) \
+  (set[i / BITSET_WORD_BITS] & ((bitset_word_t) 1 << i % BITSET_WORD_BITS))
+#define bitset_empty(set) memset (set, '\0', sizeof (bitset_t))
+#define bitset_set_all(set) memset (set, '\xff', sizeof (bitset_t))
+#define bitset_copy(dest,src) memcpy (dest, src, sizeof (bitset_t))
+
+#define PREV_WORD_CONSTRAINT 0x0001
+#define PREV_NOTWORD_CONSTRAINT 0x0002
+#define NEXT_WORD_CONSTRAINT 0x0004
+#define NEXT_NOTWORD_CONSTRAINT 0x0008
+#define PREV_NEWLINE_CONSTRAINT 0x0010
+#define NEXT_NEWLINE_CONSTRAINT 0x0020
+#define PREV_BEGBUF_CONSTRAINT 0x0040
+#define NEXT_ENDBUF_CONSTRAINT 0x0080
+#define WORD_DELIM_CONSTRAINT 0x0100
+#define NOT_WORD_DELIM_CONSTRAINT 0x0200
+
+typedef enum
+{
+  INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+  WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+  WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+  INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+  LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
+  LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
+  BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
+  BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
+  WORD_DELIM = WORD_DELIM_CONSTRAINT,
+  NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT
+} re_context_type;
+
+typedef struct
+{
+  int alloc;
+  int nelem;
+  int *elems;
+} re_node_set;
+
+typedef enum
+{
+  NON_TYPE = 0,
+
+  /* Node type, These are used by token, node, tree.  */
+  CHARACTER = 1,
+  END_OF_RE = 2,
+  SIMPLE_BRACKET = 3,
+  OP_BACK_REF = 4,
+  OP_PERIOD = 5,
+#ifdef RE_ENABLE_I18N
+  COMPLEX_BRACKET = 6,
+  OP_UTF8_PERIOD = 7,
+#endif /* RE_ENABLE_I18N */
+
+  /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used
+     when the debugger shows values of this enum type.  */
+#define EPSILON_BIT 8
+  OP_OPEN_SUBEXP = EPSILON_BIT | 0,
+  OP_CLOSE_SUBEXP = EPSILON_BIT | 1,
+  OP_ALT = EPSILON_BIT | 2,
+  OP_DUP_ASTERISK = EPSILON_BIT | 3,
+  ANCHOR = EPSILON_BIT | 4,
+
+  /* Tree type, these are used only by tree. */
+  CONCAT = 16,
+  SUBEXP = 17,
+
+  /* Token type, these are used only by token.  */
+  OP_DUP_PLUS = 18,
+  OP_DUP_QUESTION,
+  OP_OPEN_BRACKET,
+  OP_CLOSE_BRACKET,
+  OP_CHARSET_RANGE,
+  OP_OPEN_DUP_NUM,
+  OP_CLOSE_DUP_NUM,
+  OP_NON_MATCH_LIST,
+  OP_OPEN_COLL_ELEM,
+  OP_CLOSE_COLL_ELEM,
+  OP_OPEN_EQUIV_CLASS,
+  OP_CLOSE_EQUIV_CLASS,
+  OP_OPEN_CHAR_CLASS,
+  OP_CLOSE_CHAR_CLASS,
+  OP_WORD,
+  OP_NOTWORD,
+  OP_SPACE,
+  OP_NOTSPACE,
+  BACK_SLASH
+
+} re_token_type_t;
+
+#ifdef RE_ENABLE_I18N
+typedef struct
+{
+  /* Multibyte characters.  */
+  wchar_t *mbchars;
+
+  /* Collating symbols.  */
+# ifdef _LIBC
+  int32_t *coll_syms;
+# endif
+
+  /* Equivalence classes. */
+# ifdef _LIBC
+  int32_t *equiv_classes;
+# endif
+
+  /* Range expressions. */
+# ifdef _LIBC
+  uint32_t *range_starts;
+  uint32_t *range_ends;
+# else /* not _LIBC */
+  wchar_t *range_starts;
+  wchar_t *range_ends;
+# endif /* not _LIBC */
+
+  /* Character classes. */
+  wctype_t *char_classes;
+
+  /* If this character set is the non-matching list.  */
+  unsigned int non_match : 1;
+
+  /* # of multibyte characters.  */
+  int nmbchars;
+
+  /* # of collating symbols.  */
+  int ncoll_syms;
+
+  /* # of equivalence classes. */
+  int nequiv_classes;
+
+  /* # of range expressions. */
+  int nranges;
+
+  /* # of character classes. */
+  int nchar_classes;
+} re_charset_t;
+#endif /* RE_ENABLE_I18N */
+
+typedef struct
+{
+  union
+  {
+    unsigned char c;		/* for CHARACTER */
+    re_bitset_ptr_t sbcset;	/* for SIMPLE_BRACKET */
+#ifdef RE_ENABLE_I18N
+    re_charset_t *mbcset;	/* for COMPLEX_BRACKET */
+#endif /* RE_ENABLE_I18N */
+    int idx;			/* for BACK_REF */
+    re_context_type ctx_type;	/* for ANCHOR */
+  } opr;
+#if __GNUC__ >= 2
+  re_token_type_t type : 8;
+#else
+  re_token_type_t type;
+#endif
+  unsigned int constraint : 10;	/* context constraint */
+  unsigned int duplicated : 1;
+  unsigned int opt_subexp : 1;
+#ifdef RE_ENABLE_I18N
+  unsigned int accept_mb : 1;
+  /* These 2 bits can be moved into the union if needed (e.g. if running out
+     of bits; move opr.c to opr.c.c and move the flags to opr.c.flags).  */
+  unsigned int mb_partial : 1;
+#endif
+  unsigned int word_char : 1;
+} re_token_t;
+
+#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT)
+
+struct re_string_t
+{
+  /* Indicate the raw buffer which is the original string passed as an
+     argument of regexec(), re_search(), etc..  */
+  const unsigned char *raw_mbs;
+  /* Store the multibyte string.  In case of "case insensitive mode" like
+     REG_ICASE, upper cases of the string are stored, otherwise MBS points
+     the same address that RAW_MBS points.  */
+  unsigned char *mbs;
+#ifdef RE_ENABLE_I18N
+  /* Store the wide character string which is corresponding to MBS.  */
+  wint_t *wcs;
+  int *offsets;
+  mbstate_t cur_state;
+#endif
+  /* Index in RAW_MBS.  Each character mbs[i] corresponds to
+     raw_mbs[raw_mbs_idx + i].  */
+  int raw_mbs_idx;
+  /* The length of the valid characters in the buffers.  */
+  int valid_len;
+  /* The corresponding number of bytes in raw_mbs array.  */
+  int valid_raw_len;
+  /* The length of the buffers MBS and WCS.  */
+  int bufs_len;
+  /* The index in MBS, which is updated by re_string_fetch_byte.  */
+  int cur_idx;
+  /* length of RAW_MBS array.  */
+  int raw_len;
+  /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN.  */
+  int len;
+  /* End of the buffer may be shorter than its length in the cases such
+     as re_match_2, re_search_2.  Then, we use STOP for end of the buffer
+     instead of LEN.  */
+  int raw_stop;
+  /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS.  */
+  int stop;
+
+  /* The context of mbs[0].  We store the context independently, since
+     the context of mbs[0] may be different from raw_mbs[0], which is
+     the beginning of the input string.  */
+  unsigned int tip_context;
+  /* The translation passed as a part of an argument of re_compile_pattern.  */
+  RE_TRANSLATE_TYPE trans;
+  /* Copy of re_dfa_t's word_char.  */
+  re_const_bitset_ptr_t word_char;
+  /* 1 if REG_ICASE.  */
+  unsigned char icase;
+  unsigned char is_utf8;
+  unsigned char map_notascii;
+  unsigned char mbs_allocated;
+  unsigned char offsets_needed;
+  unsigned char newline_anchor;
+  unsigned char word_ops_used;
+  int mb_cur_max;
+};
+typedef struct re_string_t re_string_t;
+
+
+struct re_dfa_t;
+typedef struct re_dfa_t re_dfa_t;
+
+#ifndef _LIBC
+# ifdef __i386__
+#  define internal_function   __attribute ((regparm (3), stdcall))
+# else
+#  define internal_function
+# endif
+#endif
+
+#ifndef NOT_IN_libc
+static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
+						int new_buf_len)
+     internal_function;
+# ifdef RE_ENABLE_I18N
+static void build_wcs_buffer (re_string_t *pstr) internal_function;
+static int build_wcs_upper_buffer (re_string_t *pstr) internal_function;
+# endif /* RE_ENABLE_I18N */
+static void build_upper_buffer (re_string_t *pstr) internal_function;
+static void re_string_translate_buffer (re_string_t *pstr) internal_function;
+static unsigned int re_string_context_at (const re_string_t *input, int idx,
+					  int eflags)
+     internal_function __attribute ((pure));
+#endif
+#define re_string_peek_byte(pstr, offset) \
+  ((pstr)->mbs[(pstr)->cur_idx + offset])
+#define re_string_fetch_byte(pstr) \
+  ((pstr)->mbs[(pstr)->cur_idx++])
+#define re_string_first_byte(pstr, idx) \
+  ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF)
+#define re_string_is_single_byte_char(pstr, idx) \
+  ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \
+				|| (pstr)->wcs[(idx) + 1] != WEOF))
+#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
+#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
+#define re_string_get_buffer(pstr) ((pstr)->mbs)
+#define re_string_length(pstr) ((pstr)->len)
+#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx])
+#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
+#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
+
+#ifdef HAVE_ALLOCA_H
+ #include <alloca.h>
+#endif
+
+#ifndef _LIBC
+# if HAVE_ALLOCA
+/* The OS usually guarantees only one guard page at the bottom of the stack,
+   and a page size can be as small as 4096 bytes.  So we cannot safely
+   allocate anything larger than 4096 bytes.  Also care for the possibility
+   of a few compiler-allocated temporary stack slots.  */
+#  define __libc_use_alloca(n) ((n) < 4032)
+# else
+/* alloca is implemented with malloc, so just use malloc.  */
+#  define __libc_use_alloca(n) 0
+# endif
+#endif
+
+#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
+#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t)))
+#define re_free(p) free (p)
+
+struct bin_tree_t
+{
+  struct bin_tree_t *parent;
+  struct bin_tree_t *left;
+  struct bin_tree_t *right;
+  struct bin_tree_t *first;
+  struct bin_tree_t *next;
+
+  re_token_t token;
+
+  /* `node_idx' is the index in dfa->nodes, if `type' == 0.
+     Otherwise `type' indicate the type of this node.  */
+  int node_idx;
+};
+typedef struct bin_tree_t bin_tree_t;
+
+#define BIN_TREE_STORAGE_SIZE \
+  ((1024 - sizeof (void *)) / sizeof (bin_tree_t))
+
+struct bin_tree_storage_t
+{
+  struct bin_tree_storage_t *next;
+  bin_tree_t data[BIN_TREE_STORAGE_SIZE];
+};
+typedef struct bin_tree_storage_t bin_tree_storage_t;
+
+#define CONTEXT_WORD 1
+#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
+#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
+#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
+
+#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
+#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
+#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
+#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
+#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
+
+#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
+#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
+#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_')
+#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
+
+#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
+ ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+  || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+  || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
+  || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
+
+#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
+ ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+  || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+  || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
+  || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
+
+struct re_dfastate_t
+{
+  unsigned int hash;
+  re_node_set nodes;
+  re_node_set non_eps_nodes;
+  re_node_set inveclosure;
+  re_node_set *entrance_nodes;
+  struct re_dfastate_t **trtable, **word_trtable;
+  unsigned int context : 4;
+  unsigned int halt : 1;
+  /* If this state can accept `multi byte'.
+     Note that we refer to multibyte characters, and multi character
+     collating elements as `multi byte'.  */
+  unsigned int accept_mb : 1;
+  /* If this state has backreference node(s).  */
+  unsigned int has_backref : 1;
+  unsigned int has_constraint : 1;
+};
+typedef struct re_dfastate_t re_dfastate_t;
+
+struct re_state_table_entry
+{
+  int num;
+  int alloc;
+  re_dfastate_t **array;
+};
+
+/* Array type used in re_sub_match_last_t and re_sub_match_top_t.  */
+
+typedef struct
+{
+  int next_idx;
+  int alloc;
+  re_dfastate_t **array;
+} state_array_t;
+
+/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP.  */
+
+typedef struct
+{
+  int node;
+  int str_idx; /* The position NODE match at.  */
+  state_array_t path;
+} re_sub_match_last_t;
+
+/* Store information about the node NODE whose type is OP_OPEN_SUBEXP.
+   And information about the node, whose type is OP_CLOSE_SUBEXP,
+   corresponding to NODE is stored in LASTS.  */
+
+typedef struct
+{
+  int str_idx;
+  int node;
+  state_array_t *path;
+  int alasts; /* Allocation size of LASTS.  */
+  int nlasts; /* The number of LASTS.  */
+  re_sub_match_last_t **lasts;
+} re_sub_match_top_t;
+
+struct re_backref_cache_entry
+{
+  int node;
+  int str_idx;
+  int subexp_from;
+  int subexp_to;
+  char more;
+  char unused;
+  unsigned short int eps_reachable_subexps_map;
+};
+
+typedef struct
+{
+  /* The string object corresponding to the input string.  */
+  re_string_t input;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+  const re_dfa_t *const dfa;
+#else
+  const re_dfa_t *dfa;
+#endif
+  /* EFLAGS of the argument of regexec.  */
+  int eflags;
+  /* Where the matching ends.  */
+  int match_last;
+  int last_node;
+  /* The state log used by the matcher.  */
+  re_dfastate_t **state_log;
+  int state_log_top;
+  /* Back reference cache.  */
+  int nbkref_ents;
+  int abkref_ents;
+  struct re_backref_cache_entry *bkref_ents;
+  int max_mb_elem_len;
+  int nsub_tops;
+  int asub_tops;
+  re_sub_match_top_t **sub_tops;
+} re_match_context_t;
+
+typedef struct
+{
+  re_dfastate_t **sifted_states;
+  re_dfastate_t **limited_states;
+  int last_node;
+  int last_str_idx;
+  re_node_set limits;
+} re_sift_context_t;
+
+struct re_fail_stack_ent_t
+{
+  int idx;
+  int node;
+  regmatch_t *regs;
+  re_node_set eps_via_nodes;
+};
+
+struct re_fail_stack_t
+{
+  int num;
+  int alloc;
+  struct re_fail_stack_ent_t *stack;
+};
+
+struct re_dfa_t
+{
+  re_token_t *nodes;
+  size_t nodes_alloc;
+  size_t nodes_len;
+  int *nexts;
+  int *org_indices;
+  re_node_set *edests;
+  re_node_set *eclosures;
+  re_node_set *inveclosures;
+  struct re_state_table_entry *state_table;
+  re_dfastate_t *init_state;
+  re_dfastate_t *init_state_word;
+  re_dfastate_t *init_state_nl;
+  re_dfastate_t *init_state_begbuf;
+  bin_tree_t *str_tree;
+  bin_tree_storage_t *str_tree_storage;
+  re_bitset_ptr_t sb_char;
+  int str_tree_storage_idx;
+
+  /* number of subexpressions `re_nsub' is in regex_t.  */
+  unsigned int state_hash_mask;
+  int init_node;
+  int nbackref; /* The number of backreference in this dfa.  */
+
+  /* Bitmap expressing which backreference is used.  */
+  bitset_word_t used_bkref_map;
+  bitset_word_t completed_bkref_map;
+
+  unsigned int has_plural_match : 1;
+  /* If this dfa has "multibyte node", which is a backreference or
+     a node which can accept multibyte character or multi character
+     collating element.  */
+  unsigned int has_mb_node : 1;
+  unsigned int is_utf8 : 1;
+  unsigned int map_notascii : 1;
+  unsigned int word_ops_used : 1;
+  int mb_cur_max;
+  bitset_t word_char;
+  reg_syntax_t syntax;
+  int *subexp_map;
+#ifdef DEBUG
+  char* re_str;
+#endif
+  __libc_lock_define (, lock)
+};
+
+#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
+#define re_node_set_remove(set,id) \
+  (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1))
+#define re_node_set_empty(p) ((p)->nelem = 0)
+#define re_node_set_free(set) re_free ((set)->elems)
+

+
+typedef enum
+{
+  SB_CHAR,
+  MB_CHAR,
+  EQUIV_CLASS,
+  COLL_SYM,
+  CHAR_CLASS
+} bracket_elem_type;
+
+typedef struct
+{
+  bracket_elem_type type;
+  union
+  {
+    unsigned char ch;
+    unsigned char *name;
+    wchar_t wch;
+  } opr;
+} bracket_elem_t;
+
+
+/* Inline functions for bitset operation.  */
+static inline void
+bitset_not (bitset_t set)
+{
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+    set[bitset_i] = ~set[bitset_i];
+}
+
+static inline void
+bitset_merge (bitset_t dest, const bitset_t src)
+{
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+    dest[bitset_i] |= src[bitset_i];
+}
+
+static inline void
+bitset_mask (bitset_t dest, const bitset_t src)
+{
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+    dest[bitset_i] &= src[bitset_i];
+}
+
+#ifdef RE_ENABLE_I18N
+/* Inline functions for re_string.  */
+static inline int
+internal_function __attribute ((pure))
+re_string_char_size_at (const re_string_t *pstr, int idx)
+{
+  int byte_idx;
+  if (pstr->mb_cur_max == 1)
+    return 1;
+  for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx)
+    if (pstr->wcs[idx + byte_idx] != WEOF)
+      break;
+  return byte_idx;
+}
+
+static inline wint_t
+internal_function __attribute ((pure))
+re_string_wchar_at (const re_string_t *pstr, int idx)
+{
+  if (pstr->mb_cur_max == 1)
+    return (wint_t) pstr->mbs[idx];
+  return (wint_t) pstr->wcs[idx];
+}
+
+# ifndef NOT_IN_libc
+static int
+internal_function __attribute ((pure))
+re_string_elem_size_at (const re_string_t *pstr, int idx)
+{
+#  ifdef _LIBC
+  const unsigned char *p, *extra;
+  const int32_t *table, *indirect;
+  int32_t tmp;
+#   include <locale/weight.h>
+  uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+
+  if (nrules != 0)
+    {
+      table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+      extra = (const unsigned char *)
+	_NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+      indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+						_NL_COLLATE_INDIRECTMB);
+      p = pstr->mbs + idx;
+      tmp = findidx (&p);
+      return p - pstr->mbs - idx;
+    }
+  else
+#  endif /* _LIBC */
+    return 1;
+}
+# endif
+#endif /* RE_ENABLE_I18N */
+
+#endif /*  _REGEX_INTERNAL_H */
diff --git a/regex/regexec.c b/regex/regexec.c
new file mode 100755
index 0000000..135efe7
--- /dev/null
+++ b/regex/regexec.c
@@ -0,0 +1,4333 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu at yamato.ibm.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
+				     int n) internal_function;
+static void match_ctx_clean (re_match_context_t *mctx) internal_function;
+static void match_ctx_free (re_match_context_t *cache) internal_function;
+static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node,
+					  int str_idx, int from, int to)
+     internal_function;
+static int search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx)
+     internal_function;
+static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node,
+					   int str_idx) internal_function;
+static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop,
+						   int node, int str_idx)
+     internal_function;
+static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+			   re_dfastate_t **limited_sts, int last_node,
+			   int last_str_idx)
+     internal_function;
+static reg_errcode_t re_search_internal (const regex_t *preg,
+					 const char *string, int length,
+					 int start, int range, int stop,
+					 size_t nmatch, regmatch_t pmatch[],
+					 int eflags) internal_function;
+static int re_search_2_stub (struct re_pattern_buffer *bufp,
+			     const char *string1, int length1,
+			     const char *string2, int length2,
+			     int start, int range, struct re_registers *regs,
+			     int stop, int ret_len) internal_function;
+static int re_search_stub (struct re_pattern_buffer *bufp,
+			   const char *string, int length, int start,
+			   int range, int stop, struct re_registers *regs,
+			   int ret_len) internal_function;
+static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch,
+			      int nregs, int regs_allocated) internal_function;
+static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx)
+     internal_function;
+static int check_matching (re_match_context_t *mctx, int fl_longest_match,
+			   int *p_match_first) internal_function;
+static int check_halt_state_context (const re_match_context_t *mctx,
+				     const re_dfastate_t *state, int idx)
+     internal_function;
+static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+			 regmatch_t *prev_idx_match, int cur_node,
+			 int cur_idx, int nmatch) internal_function;
+static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs,
+				      int str_idx, int dest_node, int nregs,
+				      regmatch_t *regs,
+				      re_node_set *eps_via_nodes)
+     internal_function;
+static reg_errcode_t set_regs (const regex_t *preg,
+			       const re_match_context_t *mctx,
+			       size_t nmatch, regmatch_t *pmatch,
+			       int fl_backtrack) internal_function;
+static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs)
+     internal_function;
+
+#ifdef RE_ENABLE_I18N
+static int sift_states_iter_mb (const re_match_context_t *mctx,
+				re_sift_context_t *sctx,
+				int node_idx, int str_idx, int max_str_idx)
+     internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t sift_states_backward (const re_match_context_t *mctx,
+					   re_sift_context_t *sctx)
+     internal_function;
+static reg_errcode_t build_sifted_states (const re_match_context_t *mctx,
+					  re_sift_context_t *sctx, int str_idx,
+					  re_node_set *cur_dest)
+     internal_function;
+static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx,
+					      re_sift_context_t *sctx,
+					      int str_idx,
+					      re_node_set *dest_nodes)
+     internal_function;
+static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa,
+					    re_node_set *dest_nodes,
+					    const re_node_set *candidates)
+     internal_function;
+static int check_dst_limits (const re_match_context_t *mctx,
+			     re_node_set *limits,
+			     int dst_node, int dst_idx, int src_node,
+			     int src_idx) internal_function;
+static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx,
+					int boundaries, int subexp_idx,
+					int from_node, int bkref_idx)
+     internal_function;
+static int check_dst_limits_calc_pos (const re_match_context_t *mctx,
+				      int limit, int subexp_idx,
+				      int node, int str_idx,
+				      int bkref_idx) internal_function;
+static reg_errcode_t check_subexp_limits (const re_dfa_t *dfa,
+					  re_node_set *dest_nodes,
+					  const re_node_set *candidates,
+					  re_node_set *limits,
+					  struct re_backref_cache_entry *bkref_ents,
+					  int str_idx) internal_function;
+static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx,
+					re_sift_context_t *sctx,
+					int str_idx, const re_node_set *candidates)
+     internal_function;
+static reg_errcode_t merge_state_array (const re_dfa_t *dfa,
+					re_dfastate_t **dst,
+					re_dfastate_t **src, int num)
+     internal_function;
+static re_dfastate_t *find_recover_state (reg_errcode_t *err,
+					 re_match_context_t *mctx) internal_function;
+static re_dfastate_t *transit_state (reg_errcode_t *err,
+				     re_match_context_t *mctx,
+				     re_dfastate_t *state) internal_function;
+static re_dfastate_t *merge_state_with_log (reg_errcode_t *err,
+					    re_match_context_t *mctx,
+					    re_dfastate_t *next_state)
+     internal_function;
+static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx,
+						re_node_set *cur_nodes,
+						int str_idx) internal_function;
+#if 0
+static re_dfastate_t *transit_state_sb (reg_errcode_t *err,
+					re_match_context_t *mctx,
+					re_dfastate_t *pstate)
+     internal_function;
+#endif
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t transit_state_mb (re_match_context_t *mctx,
+				       re_dfastate_t *pstate)
+     internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t transit_state_bkref (re_match_context_t *mctx,
+					  const re_node_set *nodes)
+     internal_function;
+static reg_errcode_t get_subexp (re_match_context_t *mctx,
+				 int bkref_node, int bkref_str_idx)
+     internal_function;
+static reg_errcode_t get_subexp_sub (re_match_context_t *mctx,
+				     const re_sub_match_top_t *sub_top,
+				     re_sub_match_last_t *sub_last,
+				     int bkref_node, int bkref_str)
+     internal_function;
+static int find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+			     int subexp_idx, int type) internal_function;
+static reg_errcode_t check_arrival (re_match_context_t *mctx,
+				    state_array_t *path, int top_node,
+				    int top_str, int last_node, int last_str,
+				    int type) internal_function;
+static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx,
+						   int str_idx,
+						   re_node_set *cur_nodes,
+						   re_node_set *next_nodes)
+     internal_function;
+static reg_errcode_t check_arrival_expand_ecl (const re_dfa_t *dfa,
+					       re_node_set *cur_nodes,
+					       int ex_subexp, int type)
+     internal_function;
+static reg_errcode_t check_arrival_expand_ecl_sub (const re_dfa_t *dfa,
+						   re_node_set *dst_nodes,
+						   int target, int ex_subexp,
+						   int type) internal_function;
+static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx,
+					 re_node_set *cur_nodes, int cur_str,
+					 int subexp_num, int type)
+     internal_function;
+static int build_trtable (const re_dfa_t *dfa,
+			  re_dfastate_t *state) internal_function;
+#ifdef RE_ENABLE_I18N
+static int check_node_accept_bytes (const re_dfa_t *dfa, int node_idx,
+				    const re_string_t *input, int idx)
+     internal_function;
+# ifdef _LIBC
+static unsigned int find_collation_sequence_value (const unsigned char *mbs,
+						   size_t name_len)
+     internal_function;
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+static int group_nodes_into_DFAstates (const re_dfa_t *dfa,
+				       const re_dfastate_t *state,
+				       re_node_set *states_node,
+				       bitset_t *states_ch) internal_function;
+static int check_node_accept (const re_match_context_t *mctx,
+			      const re_token_t *node, int idx)
+     internal_function;
+static reg_errcode_t extend_buffers (re_match_context_t *mctx)
+     internal_function;
+

+/* Entry point for POSIX code.  */
+
+/* regexec searches for a given pattern, specified by PREG, in the
+   string STRING.
+
+   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
+   least NMATCH elements, and we set them to the offsets of the
+   corresponding matched substrings.
+
+   EFLAGS specifies `execution flags' which affect matching: if
+   REG_NOTBOL is set, then ^ does not match at the beginning of the
+   string; if REG_NOTEOL is set, then $ does not match at the end.
+
+   We return 0 if we find a match and REG_NOMATCH if not.  */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+    const regex_t *__restrict preg;
+    const char *__restrict string;
+    size_t nmatch;
+    regmatch_t pmatch[];
+    int eflags;
+{
+  reg_errcode_t err;
+  int start, length;
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+
+  if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND))
+    return REG_BADPAT;
+
+  if (eflags & REG_STARTEND)
+    {
+      start = pmatch[0].rm_so;
+      length = pmatch[0].rm_eo;
+    }
+  else
+    {
+      start = 0;
+      length = strlen (string);
+    }
+
+  __libc_lock_lock (dfa->lock);
+  if (preg->no_sub)
+    err = re_search_internal (preg, string, length, start, length - start,
+			      length, 0, NULL, eflags);
+  else
+    err = re_search_internal (preg, string, length, start, length - start,
+			      length, nmatch, pmatch, eflags);
+  __libc_lock_unlock (dfa->lock);
+  return err != REG_NOERROR;
+}
+
+#ifdef _LIBC
+# include <shlib-compat.h>
+versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4);
+
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4)
+__typeof__ (__regexec) __compat_regexec;
+
+int
+attribute_compat_text_section
+__compat_regexec (const regex_t *__restrict preg,
+		  const char *__restrict string, size_t nmatch,
+		  regmatch_t pmatch[], int eflags)
+{
+  return regexec (preg, string, nmatch, pmatch,
+		  eflags & (REG_NOTBOL | REG_NOTEOL));
+}
+compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0);
+# endif
+#endif
+
+/* Entry points for GNU code.  */
+
+/* re_match, re_search, re_match_2, re_search_2
+
+   The former two functions operate on STRING with length LENGTH,
+   while the later two operate on concatenation of STRING1 and STRING2
+   with lengths LENGTH1 and LENGTH2, respectively.
+
+   re_match() matches the compiled pattern in BUFP against the string,
+   starting at index START.
+
+   re_search() first tries matching at index START, then it tries to match
+   starting from index START + 1, and so on.  The last start position tried
+   is START + RANGE.  (Thus RANGE = 0 forces re_search to operate the same
+   way as re_match().)
+
+   The parameter STOP of re_{match,search}_2 specifies that no match exceeding
+   the first STOP characters of the concatenation of the strings should be
+   concerned.
+
+   If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match
+   and all groups is stroed in REGS.  (For the "_2" variants, the offsets are
+   computed relative to the concatenation, not relative to the individual
+   strings.)
+
+   On success, re_match* functions return the length of the match, re_search*
+   return the position of the start of the match.  Return value -1 means no
+   match was found and -2 indicates an internal error.  */
+
+int
+re_match (bufp, string, length, start, regs)
+    struct re_pattern_buffer *bufp;
+    const char *string;
+    int length, start;
+    struct re_registers *regs;
+{
+  return re_search_stub (bufp, string, length, start, 0, length, regs, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match, re_match)
+#endif
+
+int
+re_search (bufp, string, length, start, range, regs)
+    struct re_pattern_buffer *bufp;
+    const char *string;
+    int length, start, range;
+    struct re_registers *regs;
+{
+  return re_search_stub (bufp, string, length, start, range, length, regs, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search, re_search)
+#endif
+
+int
+re_match_2 (bufp, string1, length1, string2, length2, start, regs, stop)
+    struct re_pattern_buffer *bufp;
+    const char *string1, *string2;
+    int length1, length2, start, stop;
+    struct re_registers *regs;
+{
+  return re_search_2_stub (bufp, string1, length1, string2, length2,
+			   start, 0, regs, stop, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match_2, re_match_2)
+#endif
+
+int
+re_search_2 (bufp, string1, length1, string2, length2, start, range, regs, stop)
+    struct re_pattern_buffer *bufp;
+    const char *string1, *string2;
+    int length1, length2, start, range, stop;
+    struct re_registers *regs;
+{
+  return re_search_2_stub (bufp, string1, length1, string2, length2,
+			   start, range, regs, stop, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search_2, re_search_2)
+#endif
+
+static int
+re_search_2_stub (bufp, string1, length1, string2, length2, start, range, regs,
+		  stop, ret_len)
+    struct re_pattern_buffer *bufp;
+    const char *string1, *string2;
+    int length1, length2, start, range, stop, ret_len;
+    struct re_registers *regs;
+{
+  const char *str;
+  int rval;
+  int len = length1 + length2;
+  int free_str = 0;
+
+  if (BE (length1 < 0 || length2 < 0 || stop < 0, 0))
+    return -2;
+
+  /* Concatenate the strings.  */
+  if (length2 > 0)
+    if (length1 > 0)
+      {
+	char *s = re_malloc (char, len);
+
+	if (BE (s == NULL, 0))
+	  return -2;
+#ifdef _LIBC
+	memcpy (__mempcpy (s, string1, length1), string2, length2);
+#else
+	memcpy (s, string1, length1);
+	memcpy (s + length1, string2, length2);
+#endif
+	str = s;
+	free_str = 1;
+      }
+    else
+      str = string2;
+  else
+    str = string1;
+
+  rval = re_search_stub (bufp, str, len, start, range, stop, regs,
+			 ret_len);
+  if (free_str)
+    re_free ((char *) str);
+  return rval;
+}
+
+/* The parameters have the same meaning as those of re_search.
+   Additional parameters:
+   If RET_LEN is nonzero the length of the match is returned (re_match style);
+   otherwise the position of the match is returned.  */
+
+static int
+re_search_stub (bufp, string, length, start, range, stop, regs, ret_len)
+    struct re_pattern_buffer *bufp;
+    const char *string;
+    int length, start, range, stop, ret_len;
+    struct re_registers *regs;
+{
+  reg_errcode_t result;
+  regmatch_t *pmatch;
+  int nregs, rval;
+  int eflags = 0;
+  re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+
+  /* Check for out-of-range.  */
+  if (BE (start < 0 || start > length, 0))
+    return -1;
+  if (BE (start + range > length, 0))
+    range = length - start;
+  else if (BE (start + range < 0, 0))
+    range = -start;
+
+  __libc_lock_lock (dfa->lock);
+
+  eflags |= (bufp->not_bol) ? REG_NOTBOL : 0;
+  eflags |= (bufp->not_eol) ? REG_NOTEOL : 0;
+
+  /* Compile fastmap if we haven't yet.  */
+  if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate)
+    re_compile_fastmap (bufp);
+
+  if (BE (bufp->no_sub, 0))
+    regs = NULL;
+
+  /* We need at least 1 register.  */
+  if (regs == NULL)
+    nregs = 1;
+  else if (BE (bufp->regs_allocated == REGS_FIXED &&
+	       regs->num_regs < bufp->re_nsub + 1, 0))
+    {
+      nregs = regs->num_regs;
+      if (BE (nregs < 1, 0))
+	{
+	  /* Nothing can be copied to regs.  */
+	  regs = NULL;
+	  nregs = 1;
+	}
+    }
+  else
+    nregs = bufp->re_nsub + 1;
+  pmatch = re_malloc (regmatch_t, nregs);
+  if (BE (pmatch == NULL, 0))
+    {
+      rval = -2;
+      goto out;
+    }
+
+  result = re_search_internal (bufp, string, length, start, range, stop,
+			       nregs, pmatch, eflags);
+
+  rval = 0;
+
+  /* I hope we needn't fill ther regs with -1's when no match was found.  */
+  if (result != REG_NOERROR)
+    rval = -1;
+  else if (regs != NULL)
+    {
+      /* If caller wants register contents data back, copy them.  */
+      bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs,
+					   bufp->regs_allocated);
+      if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0))
+	rval = -2;
+    }
+
+  if (BE (rval == 0, 1))
+    {
+      if (ret_len)
+	{
+	  assert (pmatch[0].rm_so == start);
+	  rval = pmatch[0].rm_eo - start;
+	}
+      else
+	rval = pmatch[0].rm_so;
+    }
+  re_free (pmatch);
+ out:
+  __libc_lock_unlock (dfa->lock);
+  return rval;
+}
+
+static unsigned
+re_copy_regs (regs, pmatch, nregs, regs_allocated)
+    struct re_registers *regs;
+    regmatch_t *pmatch;
+    int nregs, regs_allocated;
+{
+  int rval = REGS_REALLOCATE;
+  int i;
+  int need_regs = nregs + 1;
+  /* We need one extra element beyond `num_regs' for the `-1' marker GNU code
+     uses.  */
+
+  /* Have the register data arrays been allocated?  */
+  if (regs_allocated == REGS_UNALLOCATED)
+    { /* No.  So allocate them with malloc.  */
+      regs->start = re_malloc (regoff_t, need_regs);
+      regs->end = re_malloc (regoff_t, need_regs);
+      if (BE (regs->start == NULL, 0) || BE (regs->end == NULL, 0))
+	return REGS_UNALLOCATED;
+      regs->num_regs = need_regs;
+    }
+  else if (regs_allocated == REGS_REALLOCATE)
+    { /* Yes.  If we need more elements than were already
+	 allocated, reallocate them.  If we need fewer, just
+	 leave it alone.  */
+      if (BE (need_regs > regs->num_regs, 0))
+	{
+	  regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs);
+	  regoff_t *new_end = re_realloc (regs->end, regoff_t, need_regs);
+	  if (BE (new_start == NULL, 0) || BE (new_end == NULL, 0))
+	    return REGS_UNALLOCATED;
+	  regs->start = new_start;
+	  regs->end = new_end;
+	  regs->num_regs = need_regs;
+	}
+    }
+  else
+    {
+      assert (regs_allocated == REGS_FIXED);
+      /* This function may not be called with REGS_FIXED and nregs too big.  */
+      assert (regs->num_regs >= nregs);
+      rval = REGS_FIXED;
+    }
+
+  /* Copy the regs.  */
+  for (i = 0; i < nregs; ++i)
+    {
+      regs->start[i] = pmatch[i].rm_so;
+      regs->end[i] = pmatch[i].rm_eo;
+    }
+  for ( ; i < regs->num_regs; ++i)
+    regs->start[i] = regs->end[i] = -1;
+
+  return rval;
+}
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
+   this memory for recording register information.  STARTS and ENDS
+   must be allocated using the malloc library routine, and must each
+   be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+    struct re_pattern_buffer *bufp;
+    struct re_registers *regs;
+    unsigned num_regs;
+    regoff_t *starts, *ends;
+{
+  if (num_regs)
+    {
+      bufp->regs_allocated = REGS_REALLOCATE;
+      regs->num_regs = num_regs;
+      regs->start = starts;
+      regs->end = ends;
+    }
+  else
+    {
+      bufp->regs_allocated = REGS_UNALLOCATED;
+      regs->num_regs = 0;
+      regs->start = regs->end = (regoff_t *) 0;
+    }
+}
+#ifdef _LIBC
+weak_alias (__re_set_registers, re_set_registers)
+#endif
+

+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them unless specifically requested.  */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+int
+# ifdef _LIBC
+weak_function
+# endif
+re_exec (s)
+     const char *s;
+{
+  return 0 == regexec (&re_comp_buf, s, 0, NULL, 0);
+}
+#endif /* _REGEX_RE_COMP */
+

+/* Internal entry point.  */
+
+/* Searches for a compiled pattern PREG in the string STRING, whose
+   length is LENGTH.  NMATCH, PMATCH, and EFLAGS have the same
+   mingings with regexec.  START, and RANGE have the same meanings
+   with re_search.
+   Return REG_NOERROR if we find a match, and REG_NOMATCH if not,
+   otherwise return the error code.
+   Note: We assume front end functions already check ranges.
+   (START + RANGE >= 0 && START + RANGE <= LENGTH)  */
+
+static reg_errcode_t
+re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch,
+		    eflags)
+    const regex_t *preg;
+    const char *string;
+    int length, start, range, stop, eflags;
+    size_t nmatch;
+    regmatch_t pmatch[];
+{
+  reg_errcode_t err;
+  const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer;
+  int left_lim, right_lim, incr;
+  int fl_longest_match, match_first, match_kind, match_last = -1;
+  int extra_nmatch;
+  int sb, ch;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+  re_match_context_t mctx = { .dfa = dfa };
+#else
+  re_match_context_t mctx;
+#endif
+  char *fastmap = (preg->fastmap != NULL && preg->fastmap_accurate
+		   && range && !preg->can_be_null) ? preg->fastmap : NULL;
+  RE_TRANSLATE_TYPE t = preg->translate;
+
+#if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L))
+  memset (&mctx, '\0', sizeof (re_match_context_t));
+  mctx.dfa = dfa;
+#endif
+
+  extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0;
+  nmatch -= extra_nmatch;
+
+  /* Check if the DFA haven't been compiled.  */
+  if (BE (preg->used == 0 || dfa->init_state == NULL
+	  || dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+	  || dfa->init_state_begbuf == NULL, 0))
+    return REG_NOMATCH;
+
+#ifdef DEBUG
+  /* We assume front-end functions already check them.  */
+  assert (start + range >= 0 && start + range <= length);
+#endif
+
+  /* If initial states with non-begbuf contexts have no elements,
+     the regex must be anchored.  If preg->newline_anchor is set,
+     we'll never use init_state_nl, so do not check it.  */
+  if (dfa->init_state->nodes.nelem == 0
+      && dfa->init_state_word->nodes.nelem == 0
+      && (dfa->init_state_nl->nodes.nelem == 0
+	  || !preg->newline_anchor))
+    {
+      if (start != 0 && start + range != 0)
+        return REG_NOMATCH;
+      start = range = 0;
+    }
+
+  /* We must check the longest matching, if nmatch > 0.  */
+  fl_longest_match = (nmatch != 0 || dfa->nbackref);
+
+  err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1,
+			    preg->translate, preg->syntax & RE_ICASE, dfa);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+  mctx.input.stop = stop;
+  mctx.input.raw_stop = stop;
+  mctx.input.newline_anchor = preg->newline_anchor;
+
+  err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+
+  /* We will log all the DFA states through which the dfa pass,
+     if nmatch > 1, or this dfa has "multibyte node", which is a
+     back-reference or a node which can accept multibyte character or
+     multi character collating element.  */
+  if (nmatch > 1 || dfa->has_mb_node)
+    {
+      mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1);
+      if (BE (mctx.state_log == NULL, 0))
+	{
+	  err = REG_ESPACE;
+	  goto free_return;
+	}
+    }
+  else
+    mctx.state_log = NULL;
+
+  match_first = start;
+  mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+			   : CONTEXT_NEWLINE | CONTEXT_BEGBUF;
+
+  /* Check incrementally whether of not the input string match.  */
+  incr = (range < 0) ? -1 : 1;
+  left_lim = (range < 0) ? start + range : start;
+  right_lim = (range < 0) ? start : start + range;
+  sb = dfa->mb_cur_max == 1;
+  match_kind =
+    (fastmap
+     ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0)
+	| (range >= 0 ? 2 : 0)
+	| (t != NULL ? 1 : 0))
+     : 8);
+
+  for (;; match_first += incr)
+    {
+      err = REG_NOMATCH;
+      if (match_first < left_lim || right_lim < match_first)
+	goto free_return;
+
+      /* Advance as rapidly as possible through the string, until we
+	 find a plausible place to start matching.  This may be done
+	 with varying efficiency, so there are various possibilities:
+	 only the most common of them are specialized, in order to
+	 save on code size.  We use a switch statement for speed.  */
+      switch (match_kind)
+	{
+	case 8:
+	  /* No fastmap.  */
+	  break;
+
+	case 7:
+	  /* Fastmap with single-byte translation, match forward.  */
+	  while (BE (match_first < right_lim, 1)
+		 && !fastmap[t[(unsigned char) string[match_first]]])
+	    ++match_first;
+	  goto forward_match_found_start_or_reached_end;
+
+	case 6:
+	  /* Fastmap without translation, match forward.  */
+	  while (BE (match_first < right_lim, 1)
+		 && !fastmap[(unsigned char) string[match_first]])
+	    ++match_first;
+
+	forward_match_found_start_or_reached_end:
+	  if (BE (match_first == right_lim, 0))
+	    {
+	      ch = match_first >= length
+		       ? 0 : (unsigned char) string[match_first];
+	      if (!fastmap[t ? t[ch] : ch])
+		goto free_return;
+	    }
+	  break;
+
+	case 4:
+	case 5:
+	  /* Fastmap without multi-byte translation, match backwards.  */
+	  while (match_first >= left_lim)
+	    {
+	      ch = match_first >= length
+		       ? 0 : (unsigned char) string[match_first];
+	      if (fastmap[t ? t[ch] : ch])
+		break;
+	      --match_first;
+	    }
+	  if (match_first < left_lim)
+	    goto free_return;
+	  break;
+
+	default:
+	  /* In this case, we can't determine easily the current byte,
+	     since it might be a component byte of a multibyte
+	     character.  Then we use the constructed buffer instead.  */
+	  for (;;)
+	    {
+	      /* If MATCH_FIRST is out of the valid range, reconstruct the
+		 buffers.  */
+	      unsigned int offset = match_first - mctx.input.raw_mbs_idx;
+	      if (BE (offset >= (unsigned int) mctx.input.valid_raw_len, 0))
+		{
+		  err = re_string_reconstruct (&mctx.input, match_first,
+					       eflags);
+		  if (BE (err != REG_NOERROR, 0))
+		    goto free_return;
+
+		  offset = match_first - mctx.input.raw_mbs_idx;
+		}
+	      /* If MATCH_FIRST is out of the buffer, leave it as '\0'.
+		 Note that MATCH_FIRST must not be smaller than 0.  */
+	      ch = (match_first >= length
+		    ? 0 : re_string_byte_at (&mctx.input, offset));
+	      if (fastmap[ch])
+		break;
+	      match_first += incr;
+	      if (match_first < left_lim || match_first > right_lim)
+	        {
+	          err = REG_NOMATCH;
+	          goto free_return;
+	        }
+	    }
+	  break;
+	}
+
+      /* Reconstruct the buffers so that the matcher can assume that
+	 the matching starts from the beginning of the buffer.  */
+      err = re_string_reconstruct (&mctx.input, match_first, eflags);
+      if (BE (err != REG_NOERROR, 0))
+	goto free_return;
+
+#ifdef RE_ENABLE_I18N
+     /* Don't consider this char as a possible match start if it part,
+	yet isn't the head, of a multibyte character.  */
+      if (!sb && !re_string_first_byte (&mctx.input, 0))
+	continue;
+#endif
+
+      /* It seems to be appropriate one, then use the matcher.  */
+      /* We assume that the matching starts from 0.  */
+      mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0;
+      match_last = check_matching (&mctx, fl_longest_match,
+				   range >= 0 ? &match_first : NULL);
+      if (match_last != -1)
+	{
+	  if (BE (match_last == -2, 0))
+	    {
+	      err = REG_ESPACE;
+	      goto free_return;
+	    }
+	  else
+	    {
+	      mctx.match_last = match_last;
+	      if ((!preg->no_sub && nmatch > 1) || dfa->nbackref)
+		{
+		  re_dfastate_t *pstate = mctx.state_log[match_last];
+		  mctx.last_node = check_halt_state_context (&mctx, pstate,
+							     match_last);
+		}
+	      if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match)
+		  || dfa->nbackref)
+		{
+		  err = prune_impossible_nodes (&mctx);
+		  if (err == REG_NOERROR)
+		    break;
+		  if (BE (err != REG_NOMATCH, 0))
+		    goto free_return;
+		  match_last = -1;
+		}
+	      else
+		break; /* We found a match.  */
+	    }
+	}
+
+      match_ctx_clean (&mctx);
+    }
+
+#ifdef DEBUG
+  assert (match_last != -1);
+  assert (err == REG_NOERROR);
+#endif
+
+  /* Set pmatch[] if we need.  */
+  if (nmatch > 0)
+    {
+      int reg_idx;
+
+      /* Initialize registers.  */
+      for (reg_idx = 1; reg_idx < nmatch; ++reg_idx)
+	pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1;
+
+      /* Set the points where matching start/end.  */
+      pmatch[0].rm_so = 0;
+      pmatch[0].rm_eo = mctx.match_last;
+
+      if (!preg->no_sub && nmatch > 1)
+	{
+	  err = set_regs (preg, &mctx, nmatch, pmatch,
+			  dfa->has_plural_match && dfa->nbackref > 0);
+	  if (BE (err != REG_NOERROR, 0))
+	    goto free_return;
+	}
+
+      /* At last, add the offset to the each registers, since we slided
+	 the buffers so that we could assume that the matching starts
+	 from 0.  */
+      for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+	if (pmatch[reg_idx].rm_so != -1)
+	  {
+#ifdef RE_ENABLE_I18N
+	    if (BE (mctx.input.offsets_needed != 0, 0))
+	      {
+		pmatch[reg_idx].rm_so =
+		  (pmatch[reg_idx].rm_so == mctx.input.valid_len
+		   ? mctx.input.valid_raw_len
+		   : mctx.input.offsets[pmatch[reg_idx].rm_so]);
+		pmatch[reg_idx].rm_eo =
+		  (pmatch[reg_idx].rm_eo == mctx.input.valid_len
+		   ? mctx.input.valid_raw_len
+		   : mctx.input.offsets[pmatch[reg_idx].rm_eo]);
+	      }
+#else
+	    assert (mctx.input.offsets_needed == 0);
+#endif
+	    pmatch[reg_idx].rm_so += match_first;
+	    pmatch[reg_idx].rm_eo += match_first;
+	  }
+      for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx)
+	{
+	  pmatch[nmatch + reg_idx].rm_so = -1;
+	  pmatch[nmatch + reg_idx].rm_eo = -1;
+	}
+
+      if (dfa->subexp_map)
+        for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++)
+          if (dfa->subexp_map[reg_idx] != reg_idx)
+            {
+              pmatch[reg_idx + 1].rm_so
+                = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so;
+              pmatch[reg_idx + 1].rm_eo
+                = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo;
+            }
+    }
+
+ free_return:
+  re_free (mctx.state_log);
+  if (dfa->nbackref)
+    match_ctx_free (&mctx);
+  re_string_destruct (&mctx.input);
+  return err;
+}
+
+static reg_errcode_t
+prune_impossible_nodes (mctx)
+     re_match_context_t *mctx;
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int halt_node, match_last;
+  reg_errcode_t ret;
+  re_dfastate_t **sifted_states;
+  re_dfastate_t **lim_states = NULL;
+  re_sift_context_t sctx;
+#ifdef DEBUG
+  assert (mctx->state_log != NULL);
+#endif
+  match_last = mctx->match_last;
+  halt_node = mctx->last_node;
+  sifted_states = re_malloc (re_dfastate_t *, match_last + 1);
+  if (BE (sifted_states == NULL, 0))
+    {
+      ret = REG_ESPACE;
+      goto free_return;
+    }
+  if (dfa->nbackref)
+    {
+      lim_states = re_malloc (re_dfastate_t *, match_last + 1);
+      if (BE (lim_states == NULL, 0))
+	{
+	  ret = REG_ESPACE;
+	  goto free_return;
+	}
+      while (1)
+	{
+	  memset (lim_states, '\0',
+		  sizeof (re_dfastate_t *) * (match_last + 1));
+	  sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
+			 match_last);
+	  ret = sift_states_backward (mctx, &sctx);
+	  re_node_set_free (&sctx.limits);
+	  if (BE (ret != REG_NOERROR, 0))
+	      goto free_return;
+	  if (sifted_states[0] != NULL || lim_states[0] != NULL)
+	    break;
+	  do
+	    {
+	      --match_last;
+	      if (match_last < 0)
+		{
+		  ret = REG_NOMATCH;
+		  goto free_return;
+		}
+	    } while (mctx->state_log[match_last] == NULL
+		     || !mctx->state_log[match_last]->halt);
+	  halt_node = check_halt_state_context (mctx,
+						mctx->state_log[match_last],
+						match_last);
+	}
+      ret = merge_state_array (dfa, sifted_states, lim_states,
+			       match_last + 1);
+      re_free (lim_states);
+      lim_states = NULL;
+      if (BE (ret != REG_NOERROR, 0))
+	goto free_return;
+    }
+  else
+    {
+      sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last);
+      ret = sift_states_backward (mctx, &sctx);
+      re_node_set_free (&sctx.limits);
+      if (BE (ret != REG_NOERROR, 0))
+	goto free_return;
+    }
+  re_free (mctx->state_log);
+  mctx->state_log = sifted_states;
+  sifted_states = NULL;
+  mctx->last_node = halt_node;
+  mctx->match_last = match_last;
+  ret = REG_NOERROR;
+ free_return:
+  re_free (sifted_states);
+  re_free (lim_states);
+  return ret;
+}
+
+/* Acquire an initial state and return it.
+   We must select appropriate initial state depending on the context,
+   since initial states may have constraints like "\<", "^", etc..  */
+
+static inline re_dfastate_t *
+__attribute ((always_inline)) internal_function
+acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx,
+			    int idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  if (dfa->init_state->has_constraint)
+    {
+      unsigned int context;
+      context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags);
+      if (IS_WORD_CONTEXT (context))
+	return dfa->init_state_word;
+      else if (IS_ORDINARY_CONTEXT (context))
+	return dfa->init_state;
+      else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context))
+	return dfa->init_state_begbuf;
+      else if (IS_NEWLINE_CONTEXT (context))
+	return dfa->init_state_nl;
+      else if (IS_BEGBUF_CONTEXT (context))
+	{
+	  /* It is relatively rare case, then calculate on demand.  */
+	  return re_acquire_state_context (err, dfa,
+					   dfa->init_state->entrance_nodes,
+					   context);
+	}
+      else
+	/* Must not happen?  */
+	return dfa->init_state;
+    }
+  else
+    return dfa->init_state;
+}
+
+/* Check whether the regular expression match input string INPUT or not,
+   and return the index where the matching end, return -1 if not match,
+   or return -2 in case of an error.
+   FL_LONGEST_MATCH means we want the POSIX longest matching.
+   If P_MATCH_FIRST is not NULL, and the match fails, it is set to the
+   next place where we may want to try matching.
+   Note that the matcher assume that the maching starts from the current
+   index of the buffer.  */
+
+static int
+internal_function
+check_matching (re_match_context_t *mctx, int fl_longest_match,
+		int *p_match_first)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int match = 0;
+  int match_last = -1;
+  int cur_str_idx = re_string_cur_idx (&mctx->input);
+  re_dfastate_t *cur_state;
+  int at_init_state = p_match_first != NULL;
+  int next_start_idx = cur_str_idx;
+
+  err = REG_NOERROR;
+  cur_state = acquire_init_state_context (&err, mctx, cur_str_idx);
+  /* An initial state must not be NULL (invalid).  */
+  if (BE (cur_state == NULL, 0))
+    {
+      assert (err == REG_ESPACE);
+      return -2;
+    }
+
+  if (mctx->state_log != NULL)
+    {
+      mctx->state_log[cur_str_idx] = cur_state;
+
+      /* Check OP_OPEN_SUBEXP in the initial state in case that we use them
+	 later.  E.g. Processing back references.  */
+      if (BE (dfa->nbackref, 0))
+	{
+	  at_init_state = 0;
+	  err = check_subexp_matching_top (mctx, &cur_state->nodes, 0);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+
+	  if (cur_state->has_backref)
+	    {
+	      err = transit_state_bkref (mctx, &cur_state->nodes);
+	      if (BE (err != REG_NOERROR, 0))
+	        return err;
+	    }
+	}
+    }
+
+  /* If the RE accepts NULL string.  */
+  if (BE (cur_state->halt, 0))
+    {
+      if (!cur_state->has_constraint
+	  || check_halt_state_context (mctx, cur_state, cur_str_idx))
+	{
+	  if (!fl_longest_match)
+	    return cur_str_idx;
+	  else
+	    {
+	      match_last = cur_str_idx;
+	      match = 1;
+	    }
+	}
+    }
+
+  while (!re_string_eoi (&mctx->input))
+    {
+      re_dfastate_t *old_state = cur_state;
+      int next_char_idx = re_string_cur_idx (&mctx->input) + 1;
+
+      if (BE (next_char_idx >= mctx->input.bufs_len, 0)
+          || (BE (next_char_idx >= mctx->input.valid_len, 0)
+              && mctx->input.valid_len < mctx->input.len))
+        {
+          err = extend_buffers (mctx);
+          if (BE (err != REG_NOERROR, 0))
+	    {
+	      assert (err == REG_ESPACE);
+	      return -2;
+	    }
+        }
+
+      cur_state = transit_state (&err, mctx, cur_state);
+      if (mctx->state_log != NULL)
+	cur_state = merge_state_with_log (&err, mctx, cur_state);
+
+      if (cur_state == NULL)
+	{
+	  /* Reached the invalid state or an error.  Try to recover a valid
+	     state using the state log, if available and if we have not
+	     already found a valid (even if not the longest) match.  */
+	  if (BE (err != REG_NOERROR, 0))
+	    return -2;
+
+	  if (mctx->state_log == NULL
+	      || (match && !fl_longest_match)
+	      || (cur_state = find_recover_state (&err, mctx)) == NULL)
+	    break;
+	}
+
+      if (BE (at_init_state, 0))
+	{
+	  if (old_state == cur_state)
+	    next_start_idx = next_char_idx;
+	  else
+	    at_init_state = 0;
+	}
+
+      if (cur_state->halt)
+	{
+	  /* Reached a halt state.
+	     Check the halt state can satisfy the current context.  */
+	  if (!cur_state->has_constraint
+	      || check_halt_state_context (mctx, cur_state,
+					   re_string_cur_idx (&mctx->input)))
+	    {
+	      /* We found an appropriate halt state.  */
+	      match_last = re_string_cur_idx (&mctx->input);
+	      match = 1;
+
+	      /* We found a match, do not modify match_first below.  */
+	      p_match_first = NULL;
+	      if (!fl_longest_match)
+		break;
+	    }
+	}
+    }
+
+  if (p_match_first)
+    *p_match_first += next_start_idx;
+
+  return match_last;
+}
+
+/* Check NODE match the current context.  */
+
+static int
+internal_function
+check_halt_node_context (const re_dfa_t *dfa, int node, unsigned int context)
+{
+  re_token_type_t type = dfa->nodes[node].type;
+  unsigned int constraint = dfa->nodes[node].constraint;
+  if (type != END_OF_RE)
+    return 0;
+  if (!constraint)
+    return 1;
+  if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context))
+    return 0;
+  return 1;
+}
+
+/* Check the halt state STATE match the current context.
+   Return 0 if not match, if the node, STATE has, is a halt node and
+   match the context, return the node.  */
+
+static int
+internal_function
+check_halt_state_context (const re_match_context_t *mctx,
+			  const re_dfastate_t *state, int idx)
+{
+  int i;
+  unsigned int context;
+#ifdef DEBUG
+  assert (state->halt);
+#endif
+  context = re_string_context_at (&mctx->input, idx, mctx->eflags);
+  for (i = 0; i < state->nodes.nelem; ++i)
+    if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context))
+      return state->nodes.elems[i];
+  return 0;
+}
+
+/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA
+   corresponding to the DFA).
+   Return the destination node, and update EPS_VIA_NODES, return -1 in case
+   of errors.  */
+
+static int
+internal_function
+proceed_next_node (const re_match_context_t *mctx, int nregs, regmatch_t *regs,
+		   int *pidx, int node, re_node_set *eps_via_nodes,
+		   struct re_fail_stack_t *fs)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int i, err;
+  if (IS_EPSILON_NODE (dfa->nodes[node].type))
+    {
+      re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes;
+      re_node_set *edests = &dfa->edests[node];
+      int dest_node;
+      err = re_node_set_insert (eps_via_nodes, node);
+      if (BE (err < 0, 0))
+	return -2;
+      /* Pick up a valid destination, or return -1 if none is found.  */
+      for (dest_node = -1, i = 0; i < edests->nelem; ++i)
+	{
+	  int candidate = edests->elems[i];
+	  if (!re_node_set_contains (cur_nodes, candidate))
+	    continue;
+          if (dest_node == -1)
+	    dest_node = candidate;
+
+          else
+	    {
+	      /* In order to avoid infinite loop like "(a*)*", return the second
+	         epsilon-transition if the first was already considered.  */
+	      if (re_node_set_contains (eps_via_nodes, dest_node))
+	        return candidate;
+
+	      /* Otherwise, push the second epsilon-transition on the fail stack.  */
+	      else if (fs != NULL
+		       && push_fail_stack (fs, *pidx, candidate, nregs, regs,
+				           eps_via_nodes))
+		return -2;
+
+	      /* We know we are going to exit.  */
+	      break;
+	    }
+	}
+      return dest_node;
+    }
+  else
+    {
+      int naccepted = 0;
+      re_token_type_t type = dfa->nodes[node].type;
+
+#ifdef RE_ENABLE_I18N
+      if (dfa->nodes[node].accept_mb)
+	naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx);
+      else
+#endif /* RE_ENABLE_I18N */
+      if (type == OP_BACK_REF)
+	{
+	  int subexp_idx = dfa->nodes[node].opr.idx + 1;
+	  naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so;
+	  if (fs != NULL)
+	    {
+	      if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1)
+		return -1;
+	      else if (naccepted)
+		{
+		  char *buf = (char *) re_string_get_buffer (&mctx->input);
+		  if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
+			      naccepted) != 0)
+		    return -1;
+		}
+	    }
+
+	  if (naccepted == 0)
+	    {
+	      int dest_node;
+	      err = re_node_set_insert (eps_via_nodes, node);
+	      if (BE (err < 0, 0))
+		return -2;
+	      dest_node = dfa->edests[node].elems[0];
+	      if (re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+					dest_node))
+		return dest_node;
+	    }
+	}
+
+      if (naccepted != 0
+	  || check_node_accept (mctx, dfa->nodes + node, *pidx))
+	{
+	  int dest_node = dfa->nexts[node];
+	  *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted;
+	  if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL
+		     || !re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+					       dest_node)))
+	    return -1;
+	  re_node_set_empty (eps_via_nodes);
+	  return dest_node;
+	}
+    }
+  return -1;
+}
+
+static reg_errcode_t
+internal_function
+push_fail_stack (struct re_fail_stack_t *fs, int str_idx, int dest_node,
+		 int nregs, regmatch_t *regs, re_node_set *eps_via_nodes)
+{
+  reg_errcode_t err;
+  int num = fs->num++;
+  if (fs->num == fs->alloc)
+    {
+      struct re_fail_stack_ent_t *new_array;
+      new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t)
+				       * fs->alloc * 2));
+      if (new_array == NULL)
+	return REG_ESPACE;
+      fs->alloc *= 2;
+      fs->stack = new_array;
+    }
+  fs->stack[num].idx = str_idx;
+  fs->stack[num].node = dest_node;
+  fs->stack[num].regs = re_malloc (regmatch_t, nregs);
+  if (fs->stack[num].regs == NULL)
+    return REG_ESPACE;
+  memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs);
+  err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes);
+  return err;
+}
+
+static int
+internal_function
+pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs,
+		regmatch_t *regs, re_node_set *eps_via_nodes)
+{
+  int num = --fs->num;
+  assert (num >= 0);
+  *pidx = fs->stack[num].idx;
+  memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs);
+  re_node_set_free (eps_via_nodes);
+  re_free (fs->stack[num].regs);
+  *eps_via_nodes = fs->stack[num].eps_via_nodes;
+  return fs->stack[num].node;
+}
+
+/* Set the positions where the subexpressions are starts/ends to registers
+   PMATCH.
+   Note: We assume that pmatch[0] is already set, and
+   pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch.  */
+
+static reg_errcode_t
+internal_function
+set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch,
+	  regmatch_t *pmatch, int fl_backtrack)
+{
+  const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer;
+  int idx, cur_node;
+  re_node_set eps_via_nodes;
+  struct re_fail_stack_t *fs;
+  struct re_fail_stack_t fs_body = { 0, 2, NULL };
+  regmatch_t *prev_idx_match;
+  int prev_idx_match_malloced = 0;
+
+#ifdef DEBUG
+  assert (nmatch > 1);
+  assert (mctx->state_log != NULL);
+#endif
+  if (fl_backtrack)
+    {
+      fs = &fs_body;
+      fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
+      if (fs->stack == NULL)
+	return REG_ESPACE;
+    }
+  else
+    fs = NULL;
+
+  cur_node = dfa->init_node;
+  re_node_set_init_empty (&eps_via_nodes);
+
+  if (__libc_use_alloca (nmatch * sizeof (regmatch_t)))
+    prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t));
+  else
+    {
+      prev_idx_match = re_malloc (regmatch_t, nmatch);
+      if (prev_idx_match == NULL)
+	{
+	  free_fail_stack_return (fs);
+	  return REG_ESPACE;
+	}
+      prev_idx_match_malloced = 1;
+    }
+  memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+
+  for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
+    {
+      update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch);
+
+      if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
+	{
+	  int reg_idx;
+	  if (fs)
+	    {
+	      for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+		if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1)
+		  break;
+	      if (reg_idx == nmatch)
+		{
+		  re_node_set_free (&eps_via_nodes);
+		  if (prev_idx_match_malloced)
+		    re_free (prev_idx_match);
+		  return free_fail_stack_return (fs);
+		}
+	      cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+					 &eps_via_nodes);
+	    }
+	  else
+	    {
+	      re_node_set_free (&eps_via_nodes);
+	      if (prev_idx_match_malloced)
+		re_free (prev_idx_match);
+	      return REG_NOERROR;
+	    }
+	}
+
+      /* Proceed to next node.  */
+      cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node,
+				    &eps_via_nodes, fs);
+
+      if (BE (cur_node < 0, 0))
+	{
+	  if (BE (cur_node == -2, 0))
+	    {
+	      re_node_set_free (&eps_via_nodes);
+	      if (prev_idx_match_malloced)
+		re_free (prev_idx_match);
+	      free_fail_stack_return (fs);
+	      return REG_ESPACE;
+	    }
+	  if (fs)
+	    cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+				       &eps_via_nodes);
+	  else
+	    {
+	      re_node_set_free (&eps_via_nodes);
+	      if (prev_idx_match_malloced)
+		re_free (prev_idx_match);
+	      return REG_NOMATCH;
+	    }
+	}
+    }
+  re_node_set_free (&eps_via_nodes);
+  if (prev_idx_match_malloced)
+    re_free (prev_idx_match);
+  return free_fail_stack_return (fs);
+}
+
+static reg_errcode_t
+internal_function
+free_fail_stack_return (struct re_fail_stack_t *fs)
+{
+  if (fs)
+    {
+      int fs_idx;
+      for (fs_idx = 0; fs_idx < fs->num; ++fs_idx)
+	{
+	  re_node_set_free (&fs->stack[fs_idx].eps_via_nodes);
+	  re_free (fs->stack[fs_idx].regs);
+	}
+      re_free (fs->stack);
+    }
+  return REG_NOERROR;
+}
+
+static void
+internal_function
+update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+	     regmatch_t *prev_idx_match, int cur_node, int cur_idx, int nmatch)
+{
+  int type = dfa->nodes[cur_node].type;
+  if (type == OP_OPEN_SUBEXP)
+    {
+      int reg_num = dfa->nodes[cur_node].opr.idx + 1;
+
+      /* We are at the first node of this sub expression.  */
+      if (reg_num < nmatch)
+	{
+	  pmatch[reg_num].rm_so = cur_idx;
+	  pmatch[reg_num].rm_eo = -1;
+	}
+    }
+  else if (type == OP_CLOSE_SUBEXP)
+    {
+      int reg_num = dfa->nodes[cur_node].opr.idx + 1;
+      if (reg_num < nmatch)
+	{
+	  /* We are at the last node of this sub expression.  */
+	  if (pmatch[reg_num].rm_so < cur_idx)
+	    {
+	      pmatch[reg_num].rm_eo = cur_idx;
+	      /* This is a non-empty match or we are not inside an optional
+		 subexpression.  Accept this right away.  */
+	      memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+	    }
+	  else
+	    {
+	      if (dfa->nodes[cur_node].opt_subexp
+		  && prev_idx_match[reg_num].rm_so != -1)
+		/* We transited through an empty match for an optional
+		   subexpression, like (a?)*, and this is not the subexp's
+		   first match.  Copy back the old content of the registers
+		   so that matches of an inner subexpression are undone as
+		   well, like in ((a?))*.  */
+		memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch);
+	      else
+		/* We completed a subexpression, but it may be part of
+		   an optional one, so do not update PREV_IDX_MATCH.  */
+		pmatch[reg_num].rm_eo = cur_idx;
+	    }
+	}
+    }
+}
+
+/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0
+   and sift the nodes in each states according to the following rules.
+   Updated state_log will be wrote to STATE_LOG.
+
+   Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if...
+     1. When STR_IDX == MATCH_LAST(the last index in the state_log):
+	If `a' isn't the LAST_NODE and `a' can't epsilon transit to
+	the LAST_NODE, we throw away the node `a'.
+     2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts
+	string `s' and transit to `b':
+	i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw
+	   away the node `a'.
+	ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is
+	    thrown away, we throw away the node `a'.
+     3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b':
+	i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the
+	   node `a'.
+	ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away,
+	    we throw away the node `a'.  */
+
+#define STATE_NODE_CONTAINS(state,node) \
+  ((state) != NULL && re_node_set_contains (&(state)->nodes, node))
+
+static reg_errcode_t
+internal_function
+sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx)
+{
+  reg_errcode_t err;
+  int null_cnt = 0;
+  int str_idx = sctx->last_str_idx;
+  re_node_set cur_dest;
+
+#ifdef DEBUG
+  assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL);
+#endif
+
+  /* Build sifted state_log[str_idx].  It has the nodes which can epsilon
+     transit to the last_node and the last_node itself.  */
+  err = re_node_set_init_1 (&cur_dest, sctx->last_node);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+
+  /* Then check each states in the state_log.  */
+  while (str_idx > 0)
+    {
+      /* Update counters.  */
+      null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0;
+      if (null_cnt > mctx->max_mb_elem_len)
+	{
+	  memset (sctx->sifted_states, '\0',
+		  sizeof (re_dfastate_t *) * str_idx);
+	  re_node_set_free (&cur_dest);
+	  return REG_NOERROR;
+	}
+      re_node_set_empty (&cur_dest);
+      --str_idx;
+
+      if (mctx->state_log[str_idx])
+	{
+	  err = build_sifted_states (mctx, sctx, str_idx, &cur_dest);
+          if (BE (err != REG_NOERROR, 0))
+	    goto free_return;
+	}
+
+      /* Add all the nodes which satisfy the following conditions:
+	 - It can epsilon transit to a node in CUR_DEST.
+	 - It is in CUR_SRC.
+	 And update state_log.  */
+      err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+      if (BE (err != REG_NOERROR, 0))
+	goto free_return;
+    }
+  err = REG_NOERROR;
+ free_return:
+  re_node_set_free (&cur_dest);
+  return err;
+}
+
+static reg_errcode_t
+internal_function
+build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx,
+		     int str_idx, re_node_set *cur_dest)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes;
+  int i;
+
+  /* Then build the next sifted state.
+     We build the next sifted state on `cur_dest', and update
+     `sifted_states[str_idx]' with `cur_dest'.
+     Note:
+     `cur_dest' is the sifted state from `state_log[str_idx + 1]'.
+     `cur_src' points the node_set of the old `state_log[str_idx]'
+     (with the epsilon nodes pre-filtered out).  */
+  for (i = 0; i < cur_src->nelem; i++)
+    {
+      int prev_node = cur_src->elems[i];
+      int naccepted = 0;
+      int ret;
+
+#ifdef DEBUG
+      re_token_type_t type = dfa->nodes[prev_node].type;
+      assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+      /* If the node may accept `multi byte'.  */
+      if (dfa->nodes[prev_node].accept_mb)
+	naccepted = sift_states_iter_mb (mctx, sctx, prev_node,
+					 str_idx, sctx->last_str_idx);
+#endif /* RE_ENABLE_I18N */
+
+      /* We don't check backreferences here.
+	 See update_cur_sifted_state().  */
+      if (!naccepted
+	  && check_node_accept (mctx, dfa->nodes + prev_node, str_idx)
+	  && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1],
+				  dfa->nexts[prev_node]))
+	naccepted = 1;
+
+      if (naccepted == 0)
+	continue;
+
+      if (sctx->limits.nelem)
+	{
+	  int to_idx = str_idx + naccepted;
+	  if (check_dst_limits (mctx, &sctx->limits,
+				dfa->nexts[prev_node], to_idx,
+				prev_node, str_idx))
+	    continue;
+	}
+      ret = re_node_set_insert (cur_dest, prev_node);
+      if (BE (ret == -1, 0))
+	return REG_ESPACE;
+    }
+
+  return REG_NOERROR;
+}
+
+/* Helper functions.  */
+
+static reg_errcode_t
+internal_function
+clean_state_log_if_needed (re_match_context_t *mctx, int next_state_log_idx)
+{
+  int top = mctx->state_log_top;
+
+  if (next_state_log_idx >= mctx->input.bufs_len
+      || (next_state_log_idx >= mctx->input.valid_len
+	  && mctx->input.valid_len < mctx->input.len))
+    {
+      reg_errcode_t err;
+      err = extend_buffers (mctx);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+    }
+
+  if (top < next_state_log_idx)
+    {
+      memset (mctx->state_log + top + 1, '\0',
+	      sizeof (re_dfastate_t *) * (next_state_log_idx - top));
+      mctx->state_log_top = next_state_log_idx;
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst,
+		   re_dfastate_t **src, int num)
+{
+  int st_idx;
+  reg_errcode_t err;
+  for (st_idx = 0; st_idx < num; ++st_idx)
+    {
+      if (dst[st_idx] == NULL)
+	dst[st_idx] = src[st_idx];
+      else if (src[st_idx] != NULL)
+	{
+	  re_node_set merged_set;
+	  err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes,
+					&src[st_idx]->nodes);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	  dst[st_idx] = re_acquire_state (&err, dfa, &merged_set);
+	  re_node_set_free (&merged_set);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+update_cur_sifted_state (const re_match_context_t *mctx,
+			 re_sift_context_t *sctx, int str_idx,
+			 re_node_set *dest_nodes)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err = REG_NOERROR;
+  const re_node_set *candidates;
+  candidates = ((mctx->state_log[str_idx] == NULL) ? NULL
+		: &mctx->state_log[str_idx]->nodes);
+
+  if (dest_nodes->nelem == 0)
+    sctx->sifted_states[str_idx] = NULL;
+  else
+    {
+      if (candidates)
+	{
+	  /* At first, add the nodes which can epsilon transit to a node in
+	     DEST_NODE.  */
+	  err = add_epsilon_src_nodes (dfa, dest_nodes, candidates);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+
+	  /* Then, check the limitations in the current sift_context.  */
+	  if (sctx->limits.nelem)
+	    {
+	      err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits,
+					 mctx->bkref_ents, str_idx);
+	      if (BE (err != REG_NOERROR, 0))
+		return err;
+	    }
+	}
+
+      sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+    }
+
+  if (candidates && mctx->state_log[str_idx]->has_backref)
+    {
+      err = sift_states_bkref (mctx, sctx, str_idx, candidates);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes,
+		       const re_node_set *candidates)
+{
+  reg_errcode_t err = REG_NOERROR;
+  int i;
+
+  re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+
+  if (!state->inveclosure.alloc)
+    {
+      err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem);
+      if (BE (err != REG_NOERROR, 0))
+        return REG_ESPACE;
+      for (i = 0; i < dest_nodes->nelem; i++)
+        re_node_set_merge (&state->inveclosure,
+			   dfa->inveclosures + dest_nodes->elems[i]);
+    }
+  return re_node_set_add_intersect (dest_nodes, candidates,
+				    &state->inveclosure);
+}
+
+static reg_errcode_t
+internal_function
+sub_epsilon_src_nodes (const re_dfa_t *dfa, int node, re_node_set *dest_nodes,
+		       const re_node_set *candidates)
+{
+    int ecl_idx;
+    reg_errcode_t err;
+    re_node_set *inv_eclosure = dfa->inveclosures + node;
+    re_node_set except_nodes;
+    re_node_set_init_empty (&except_nodes);
+    for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+      {
+	int cur_node = inv_eclosure->elems[ecl_idx];
+	if (cur_node == node)
+	  continue;
+	if (IS_EPSILON_NODE (dfa->nodes[cur_node].type))
+	  {
+	    int edst1 = dfa->edests[cur_node].elems[0];
+	    int edst2 = ((dfa->edests[cur_node].nelem > 1)
+			 ? dfa->edests[cur_node].elems[1] : -1);
+	    if ((!re_node_set_contains (inv_eclosure, edst1)
+		 && re_node_set_contains (dest_nodes, edst1))
+		|| (edst2 > 0
+		    && !re_node_set_contains (inv_eclosure, edst2)
+		    && re_node_set_contains (dest_nodes, edst2)))
+	      {
+		err = re_node_set_add_intersect (&except_nodes, candidates,
+						 dfa->inveclosures + cur_node);
+		if (BE (err != REG_NOERROR, 0))
+		  {
+		    re_node_set_free (&except_nodes);
+		    return err;
+		  }
+	      }
+	  }
+      }
+    for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+      {
+	int cur_node = inv_eclosure->elems[ecl_idx];
+	if (!re_node_set_contains (&except_nodes, cur_node))
+	  {
+	    int idx = re_node_set_contains (dest_nodes, cur_node) - 1;
+	    re_node_set_remove_at (dest_nodes, idx);
+	  }
+      }
+    re_node_set_free (&except_nodes);
+    return REG_NOERROR;
+}
+
+static int
+internal_function
+check_dst_limits (const re_match_context_t *mctx, re_node_set *limits,
+		  int dst_node, int dst_idx, int src_node, int src_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int lim_idx, src_pos, dst_pos;
+
+  int dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx);
+  int src_bkref_idx = search_cur_bkref_entry (mctx, src_idx);
+  for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+    {
+      int subexp_idx;
+      struct re_backref_cache_entry *ent;
+      ent = mctx->bkref_ents + limits->elems[lim_idx];
+      subexp_idx = dfa->nodes[ent->node].opr.idx;
+
+      dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+					   subexp_idx, dst_node, dst_idx,
+					   dst_bkref_idx);
+      src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+					   subexp_idx, src_node, src_idx,
+					   src_bkref_idx);
+
+      /* In case of:
+	 <src> <dst> ( <subexp> )
+	 ( <subexp> ) <src> <dst>
+	 ( <subexp1> <src> <subexp2> <dst> <subexp3> )  */
+      if (src_pos == dst_pos)
+	continue; /* This is unrelated limitation.  */
+      else
+	return 1;
+    }
+  return 0;
+}
+
+static int
+internal_function
+check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries,
+			     int subexp_idx, int from_node, int bkref_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  const re_node_set *eclosures = dfa->eclosures + from_node;
+  int node_idx;
+
+  /* Else, we are on the boundary: examine the nodes on the epsilon
+     closure.  */
+  for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
+    {
+      int node = eclosures->elems[node_idx];
+      switch (dfa->nodes[node].type)
+	{
+	case OP_BACK_REF:
+	  if (bkref_idx != -1)
+	    {
+	      struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx;
+	      do
+	        {
+		  int dst, cpos;
+
+		  if (ent->node != node)
+		    continue;
+
+		  if (subexp_idx < BITSET_WORD_BITS
+		      && !(ent->eps_reachable_subexps_map
+			   & ((bitset_word_t) 1 << subexp_idx)))
+		    continue;
+
+		  /* Recurse trying to reach the OP_OPEN_SUBEXP and
+		     OP_CLOSE_SUBEXP cases below.  But, if the
+		     destination node is the same node as the source
+		     node, don't recurse because it would cause an
+		     infinite loop: a regex that exhibits this behavior
+		     is ()\1*\1*  */
+		  dst = dfa->edests[node].elems[0];
+		  if (dst == from_node)
+		    {
+		      if (boundaries & 1)
+		        return -1;
+		      else /* if (boundaries & 2) */
+		        return 0;
+		    }
+
+		  cpos =
+		    check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+						 dst, bkref_idx);
+		  if (cpos == -1 /* && (boundaries & 1) */)
+		    return -1;
+		  if (cpos == 0 && (boundaries & 2))
+		    return 0;
+
+		  if (subexp_idx < BITSET_WORD_BITS)
+		    ent->eps_reachable_subexps_map
+		      &= ~((bitset_word_t) 1 << subexp_idx);
+	        }
+	      while (ent++->more);
+	    }
+	  break;
+
+	case OP_OPEN_SUBEXP:
+	  if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx)
+	    return -1;
+	  break;
+
+	case OP_CLOSE_SUBEXP:
+	  if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx)
+	    return 0;
+	  break;
+
+	default:
+	    break;
+	}
+    }
+
+  return (boundaries & 2) ? 1 : 0;
+}
+
+static int
+internal_function
+check_dst_limits_calc_pos (const re_match_context_t *mctx, int limit,
+			   int subexp_idx, int from_node, int str_idx,
+			   int bkref_idx)
+{
+  struct re_backref_cache_entry *lim = mctx->bkref_ents + limit;
+  int boundaries;
+
+  /* If we are outside the range of the subexpression, return -1 or 1.  */
+  if (str_idx < lim->subexp_from)
+    return -1;
+
+  if (lim->subexp_to < str_idx)
+    return 1;
+
+  /* If we are within the subexpression, return 0.  */
+  boundaries = (str_idx == lim->subexp_from);
+  boundaries |= (str_idx == lim->subexp_to) << 1;
+  if (boundaries == 0)
+    return 0;
+
+  /* Else, examine epsilon closure.  */
+  return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+				      from_node, bkref_idx);
+}
+
+/* Check the limitations of sub expressions LIMITS, and remove the nodes
+   which are against limitations from DEST_NODES. */
+
+static reg_errcode_t
+internal_function
+check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes,
+		     const re_node_set *candidates, re_node_set *limits,
+		     struct re_backref_cache_entry *bkref_ents, int str_idx)
+{
+  reg_errcode_t err;
+  int node_idx, lim_idx;
+
+  for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+    {
+      int subexp_idx;
+      struct re_backref_cache_entry *ent;
+      ent = bkref_ents + limits->elems[lim_idx];
+
+      if (str_idx <= ent->subexp_from || ent->str_idx < str_idx)
+	continue; /* This is unrelated limitation.  */
+
+      subexp_idx = dfa->nodes[ent->node].opr.idx;
+      if (ent->subexp_to == str_idx)
+	{
+	  int ops_node = -1;
+	  int cls_node = -1;
+	  for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+	    {
+	      int node = dest_nodes->elems[node_idx];
+	      re_token_type_t type = dfa->nodes[node].type;
+	      if (type == OP_OPEN_SUBEXP
+		  && subexp_idx == dfa->nodes[node].opr.idx)
+		ops_node = node;
+	      else if (type == OP_CLOSE_SUBEXP
+		       && subexp_idx == dfa->nodes[node].opr.idx)
+		cls_node = node;
+	    }
+
+	  /* Check the limitation of the open subexpression.  */
+	  /* Note that (ent->subexp_to = str_idx != ent->subexp_from).  */
+	  if (ops_node >= 0)
+	    {
+	      err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes,
+					   candidates);
+	      if (BE (err != REG_NOERROR, 0))
+		return err;
+	    }
+
+	  /* Check the limitation of the close subexpression.  */
+	  if (cls_node >= 0)
+	    for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+	      {
+		int node = dest_nodes->elems[node_idx];
+		if (!re_node_set_contains (dfa->inveclosures + node,
+					   cls_node)
+		    && !re_node_set_contains (dfa->eclosures + node,
+					      cls_node))
+		  {
+		    /* It is against this limitation.
+		       Remove it form the current sifted state.  */
+		    err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+						 candidates);
+		    if (BE (err != REG_NOERROR, 0))
+		      return err;
+		    --node_idx;
+		  }
+	      }
+	}
+      else /* (ent->subexp_to != str_idx)  */
+	{
+	  for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+	    {
+	      int node = dest_nodes->elems[node_idx];
+	      re_token_type_t type = dfa->nodes[node].type;
+	      if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP)
+		{
+		  if (subexp_idx != dfa->nodes[node].opr.idx)
+		    continue;
+		  /* It is against this limitation.
+		     Remove it form the current sifted state.  */
+		  err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+					       candidates);
+		  if (BE (err != REG_NOERROR, 0))
+		    return err;
+		}
+	    }
+	}
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx,
+		   int str_idx, const re_node_set *candidates)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int node_idx, node;
+  re_sift_context_t local_sctx;
+  int first_idx = search_cur_bkref_entry (mctx, str_idx);
+
+  if (first_idx == -1)
+    return REG_NOERROR;
+
+  local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized.  */
+
+  for (node_idx = 0; node_idx < candidates->nelem; ++node_idx)
+    {
+      int enabled_idx;
+      re_token_type_t type;
+      struct re_backref_cache_entry *entry;
+      node = candidates->elems[node_idx];
+      type = dfa->nodes[node].type;
+      /* Avoid infinite loop for the REs like "()\1+".  */
+      if (node == sctx->last_node && str_idx == sctx->last_str_idx)
+	continue;
+      if (type != OP_BACK_REF)
+	continue;
+
+      entry = mctx->bkref_ents + first_idx;
+      enabled_idx = first_idx;
+      do
+	{
+	  int subexp_len;
+	  int to_idx;
+	  int dst_node;
+	  int ret;
+	  re_dfastate_t *cur_state;
+
+	  if (entry->node != node)
+	    continue;
+	  subexp_len = entry->subexp_to - entry->subexp_from;
+	  to_idx = str_idx + subexp_len;
+	  dst_node = (subexp_len ? dfa->nexts[node]
+		      : dfa->edests[node].elems[0]);
+
+	  if (to_idx > sctx->last_str_idx
+	      || sctx->sifted_states[to_idx] == NULL
+	      || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node)
+	      || check_dst_limits (mctx, &sctx->limits, node,
+				   str_idx, dst_node, to_idx))
+	    continue;
+
+	  if (local_sctx.sifted_states == NULL)
+	    {
+	      local_sctx = *sctx;
+	      err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits);
+	      if (BE (err != REG_NOERROR, 0))
+		goto free_return;
+	    }
+	  local_sctx.last_node = node;
+	  local_sctx.last_str_idx = str_idx;
+	  ret = re_node_set_insert (&local_sctx.limits, enabled_idx);
+	  if (BE (ret < 0, 0))
+	    {
+	      err = REG_ESPACE;
+	      goto free_return;
+	    }
+	  cur_state = local_sctx.sifted_states[str_idx];
+	  err = sift_states_backward (mctx, &local_sctx);
+	  if (BE (err != REG_NOERROR, 0))
+	    goto free_return;
+	  if (sctx->limited_states != NULL)
+	    {
+	      err = merge_state_array (dfa, sctx->limited_states,
+				       local_sctx.sifted_states,
+				       str_idx + 1);
+	      if (BE (err != REG_NOERROR, 0))
+		goto free_return;
+	    }
+	  local_sctx.sifted_states[str_idx] = cur_state;
+	  re_node_set_remove (&local_sctx.limits, enabled_idx);
+
+	  /* mctx->bkref_ents may have changed, reload the pointer.  */
+          entry = mctx->bkref_ents + enabled_idx;
+	}
+      while (enabled_idx++, entry++->more);
+    }
+  err = REG_NOERROR;
+ free_return:
+  if (local_sctx.sifted_states != NULL)
+    {
+      re_node_set_free (&local_sctx.limits);
+    }
+
+  return err;
+}
+
+
+#ifdef RE_ENABLE_I18N
+static int
+internal_function
+sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx,
+		     int node_idx, int str_idx, int max_str_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int naccepted;
+  /* Check the node can accept `multi byte'.  */
+  naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx);
+  if (naccepted > 0 && str_idx + naccepted <= max_str_idx &&
+      !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted],
+			    dfa->nexts[node_idx]))
+    /* The node can't accept the `multi byte', or the
+       destination was already thrown away, then the node
+       could't accept the current input `multi byte'.   */
+    naccepted = 0;
+  /* Otherwise, it is sure that the node could accept
+     `naccepted' bytes input.  */
+  return naccepted;
+}
+#endif /* RE_ENABLE_I18N */
+
+

+/* Functions for state transition.  */
+
+/* Return the next state to which the current state STATE will transit by
+   accepting the current input byte, and update STATE_LOG if necessary.
+   If STATE can accept a multibyte char/collating element/back reference
+   update the destination of STATE_LOG.  */
+
+static re_dfastate_t *
+internal_function
+transit_state (reg_errcode_t *err, re_match_context_t *mctx,
+	       re_dfastate_t *state)
+{
+  re_dfastate_t **trtable;
+  unsigned char ch;
+
+#ifdef RE_ENABLE_I18N
+  /* If the current state can accept multibyte.  */
+  if (BE (state->accept_mb, 0))
+    {
+      *err = transit_state_mb (mctx, state);
+      if (BE (*err != REG_NOERROR, 0))
+	return NULL;
+    }
+#endif /* RE_ENABLE_I18N */
+
+  /* Then decide the next state with the single byte.  */
+#if 0
+  if (0)
+    /* don't use transition table  */
+    return transit_state_sb (err, mctx, state);
+#endif
+
+  /* Use transition table  */
+  ch = re_string_fetch_byte (&mctx->input);
+  for (;;)
+    {
+      trtable = state->trtable;
+      if (BE (trtable != NULL, 1))
+	return trtable[ch];
+
+      trtable = state->word_trtable;
+      if (BE (trtable != NULL, 1))
+        {
+	  unsigned int context;
+	  context
+	    = re_string_context_at (&mctx->input,
+				    re_string_cur_idx (&mctx->input) - 1,
+				    mctx->eflags);
+	  if (IS_WORD_CONTEXT (context))
+	    return trtable[ch + SBC_MAX];
+	  else
+	    return trtable[ch];
+	}
+
+      if (!build_trtable (mctx->dfa, state))
+	{
+	  *err = REG_ESPACE;
+	  return NULL;
+	}
+
+      /* Retry, we now have a transition table.  */
+    }
+}
+
+/* Update the state_log if we need */
+re_dfastate_t *
+internal_function
+merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx,
+		      re_dfastate_t *next_state)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int cur_idx = re_string_cur_idx (&mctx->input);
+
+  if (cur_idx > mctx->state_log_top)
+    {
+      mctx->state_log[cur_idx] = next_state;
+      mctx->state_log_top = cur_idx;
+    }
+  else if (mctx->state_log[cur_idx] == 0)
+    {
+      mctx->state_log[cur_idx] = next_state;
+    }
+  else
+    {
+      re_dfastate_t *pstate;
+      unsigned int context;
+      re_node_set next_nodes, *log_nodes, *table_nodes = NULL;
+      /* If (state_log[cur_idx] != 0), it implies that cur_idx is
+         the destination of a multibyte char/collating element/
+         back reference.  Then the next state is the union set of
+         these destinations and the results of the transition table.  */
+      pstate = mctx->state_log[cur_idx];
+      log_nodes = pstate->entrance_nodes;
+      if (next_state != NULL)
+        {
+          table_nodes = next_state->entrance_nodes;
+          *err = re_node_set_init_union (&next_nodes, table_nodes,
+					     log_nodes);
+          if (BE (*err != REG_NOERROR, 0))
+	    return NULL;
+        }
+      else
+        next_nodes = *log_nodes;
+      /* Note: We already add the nodes of the initial state,
+	 then we don't need to add them here.  */
+
+      context = re_string_context_at (&mctx->input,
+				      re_string_cur_idx (&mctx->input) - 1,
+				      mctx->eflags);
+      next_state = mctx->state_log[cur_idx]
+        = re_acquire_state_context (err, dfa, &next_nodes, context);
+      /* We don't need to check errors here, since the return value of
+         this function is next_state and ERR is already set.  */
+
+      if (table_nodes != NULL)
+        re_node_set_free (&next_nodes);
+    }
+
+  if (BE (dfa->nbackref, 0) && next_state != NULL)
+    {
+      /* Check OP_OPEN_SUBEXP in the current state in case that we use them
+	 later.  We must check them here, since the back references in the
+	 next state might use them.  */
+      *err = check_subexp_matching_top (mctx, &next_state->nodes,
+					cur_idx);
+      if (BE (*err != REG_NOERROR, 0))
+	return NULL;
+
+      /* If the next state has back references.  */
+      if (next_state->has_backref)
+	{
+	  *err = transit_state_bkref (mctx, &next_state->nodes);
+	  if (BE (*err != REG_NOERROR, 0))
+	    return NULL;
+	  next_state = mctx->state_log[cur_idx];
+	}
+    }
+
+  return next_state;
+}
+
+/* Skip bytes in the input that correspond to part of a
+   multi-byte match, then look in the log for a state
+   from which to restart matching.  */
+re_dfastate_t *
+internal_function
+find_recover_state (reg_errcode_t *err, re_match_context_t *mctx)
+{
+  re_dfastate_t *cur_state;
+  do
+    {
+      int max = mctx->state_log_top;
+      int cur_str_idx = re_string_cur_idx (&mctx->input);
+
+      do
+	{
+          if (++cur_str_idx > max)
+            return NULL;
+          re_string_skip_bytes (&mctx->input, 1);
+	}
+      while (mctx->state_log[cur_str_idx] == NULL);
+
+      cur_state = merge_state_with_log (err, mctx, NULL);
+    }
+  while (*err == REG_NOERROR && cur_state == NULL);
+  return cur_state;
+}
+
+/* Helper functions for transit_state.  */
+
+/* From the node set CUR_NODES, pick up the nodes whose types are
+   OP_OPEN_SUBEXP and which have corresponding back references in the regular
+   expression. And register them to use them later for evaluating the
+   correspoding back references.  */
+
+static reg_errcode_t
+internal_function
+check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes,
+			   int str_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int node_idx;
+  reg_errcode_t err;
+
+  /* TODO: This isn't efficient.
+	   Because there might be more than one nodes whose types are
+	   OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+	   nodes.
+	   E.g. RE: (a){2}  */
+  for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx)
+    {
+      int node = cur_nodes->elems[node_idx];
+      if (dfa->nodes[node].type == OP_OPEN_SUBEXP
+	  && dfa->nodes[node].opr.idx < BITSET_WORD_BITS
+	  && (dfa->used_bkref_map
+	      & ((bitset_word_t) 1 << dfa->nodes[node].opr.idx)))
+	{
+	  err = match_ctx_add_subtop (mctx, node, str_idx);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+    }
+  return REG_NOERROR;
+}
+
+#if 0
+/* Return the next state to which the current state STATE will transit by
+   accepting the current input byte.  */
+
+static re_dfastate_t *
+transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx,
+		  re_dfastate_t *state)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  re_node_set next_nodes;
+  re_dfastate_t *next_state;
+  int node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input);
+  unsigned int context;
+
+  *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1);
+  if (BE (*err != REG_NOERROR, 0))
+    return NULL;
+  for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt)
+    {
+      int cur_node = state->nodes.elems[node_cnt];
+      if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx))
+	{
+	  *err = re_node_set_merge (&next_nodes,
+				    dfa->eclosures + dfa->nexts[cur_node]);
+	  if (BE (*err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return NULL;
+	    }
+	}
+    }
+  context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags);
+  next_state = re_acquire_state_context (err, dfa, &next_nodes, context);
+  /* We don't need to check errors here, since the return value of
+     this function is next_state and ERR is already set.  */
+
+  re_node_set_free (&next_nodes);
+  re_string_skip_bytes (&mctx->input, 1);
+  return next_state;
+}
+#endif
+
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t
+internal_function
+transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int i;
+
+  for (i = 0; i < pstate->nodes.nelem; ++i)
+    {
+      re_node_set dest_nodes, *new_nodes;
+      int cur_node_idx = pstate->nodes.elems[i];
+      int naccepted, dest_idx;
+      unsigned int context;
+      re_dfastate_t *dest_state;
+
+      if (!dfa->nodes[cur_node_idx].accept_mb)
+        continue;
+
+      if (dfa->nodes[cur_node_idx].constraint)
+	{
+	  context = re_string_context_at (&mctx->input,
+					  re_string_cur_idx (&mctx->input),
+					  mctx->eflags);
+	  if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint,
+					   context))
+	    continue;
+	}
+
+      /* How many bytes the node can accept?  */
+      naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input,
+					   re_string_cur_idx (&mctx->input));
+      if (naccepted == 0)
+	continue;
+
+      /* The node can accepts `naccepted' bytes.  */
+      dest_idx = re_string_cur_idx (&mctx->input) + naccepted;
+      mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted
+			       : mctx->max_mb_elem_len);
+      err = clean_state_log_if_needed (mctx, dest_idx);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+#ifdef DEBUG
+      assert (dfa->nexts[cur_node_idx] != -1);
+#endif
+      new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx];
+
+      dest_state = mctx->state_log[dest_idx];
+      if (dest_state == NULL)
+	dest_nodes = *new_nodes;
+      else
+	{
+	  err = re_node_set_init_union (&dest_nodes,
+					dest_state->entrance_nodes, new_nodes);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+      context = re_string_context_at (&mctx->input, dest_idx - 1,
+				      mctx->eflags);
+      mctx->state_log[dest_idx]
+	= re_acquire_state_context (&err, dfa, &dest_nodes, context);
+      if (dest_state != NULL)
+	re_node_set_free (&dest_nodes);
+      if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0))
+	return err;
+    }
+  return REG_NOERROR;
+}
+#endif /* RE_ENABLE_I18N */
+
+static reg_errcode_t
+internal_function
+transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int i;
+  int cur_str_idx = re_string_cur_idx (&mctx->input);
+
+  for (i = 0; i < nodes->nelem; ++i)
+    {
+      int dest_str_idx, prev_nelem, bkc_idx;
+      int node_idx = nodes->elems[i];
+      unsigned int context;
+      const re_token_t *node = dfa->nodes + node_idx;
+      re_node_set *new_dest_nodes;
+
+      /* Check whether `node' is a backreference or not.  */
+      if (node->type != OP_BACK_REF)
+	continue;
+
+      if (node->constraint)
+	{
+	  context = re_string_context_at (&mctx->input, cur_str_idx,
+					  mctx->eflags);
+	  if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+	    continue;
+	}
+
+      /* `node' is a backreference.
+	 Check the substring which the substring matched.  */
+      bkc_idx = mctx->nbkref_ents;
+      err = get_subexp (mctx, node_idx, cur_str_idx);
+      if (BE (err != REG_NOERROR, 0))
+	goto free_return;
+
+      /* And add the epsilon closures (which is `new_dest_nodes') of
+	 the backreference to appropriate state_log.  */
+#ifdef DEBUG
+      assert (dfa->nexts[node_idx] != -1);
+#endif
+      for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx)
+	{
+	  int subexp_len;
+	  re_dfastate_t *dest_state;
+	  struct re_backref_cache_entry *bkref_ent;
+	  bkref_ent = mctx->bkref_ents + bkc_idx;
+	  if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx)
+	    continue;
+	  subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from;
+	  new_dest_nodes = (subexp_len == 0
+			    ? dfa->eclosures + dfa->edests[node_idx].elems[0]
+			    : dfa->eclosures + dfa->nexts[node_idx]);
+	  dest_str_idx = (cur_str_idx + bkref_ent->subexp_to
+			  - bkref_ent->subexp_from);
+	  context = re_string_context_at (&mctx->input, dest_str_idx - 1,
+					  mctx->eflags);
+	  dest_state = mctx->state_log[dest_str_idx];
+	  prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0
+			: mctx->state_log[cur_str_idx]->nodes.nelem);
+	  /* Add `new_dest_node' to state_log.  */
+	  if (dest_state == NULL)
+	    {
+	      mctx->state_log[dest_str_idx]
+		= re_acquire_state_context (&err, dfa, new_dest_nodes,
+					    context);
+	      if (BE (mctx->state_log[dest_str_idx] == NULL
+		      && err != REG_NOERROR, 0))
+		goto free_return;
+	    }
+	  else
+	    {
+	      re_node_set dest_nodes;
+	      err = re_node_set_init_union (&dest_nodes,
+					    dest_state->entrance_nodes,
+					    new_dest_nodes);
+	      if (BE (err != REG_NOERROR, 0))
+		{
+		  re_node_set_free (&dest_nodes);
+		  goto free_return;
+		}
+	      mctx->state_log[dest_str_idx]
+		= re_acquire_state_context (&err, dfa, &dest_nodes, context);
+	      re_node_set_free (&dest_nodes);
+	      if (BE (mctx->state_log[dest_str_idx] == NULL
+		      && err != REG_NOERROR, 0))
+		goto free_return;
+	    }
+	  /* We need to check recursively if the backreference can epsilon
+	     transit.  */
+	  if (subexp_len == 0
+	      && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem)
+	    {
+	      err = check_subexp_matching_top (mctx, new_dest_nodes,
+					       cur_str_idx);
+	      if (BE (err != REG_NOERROR, 0))
+		goto free_return;
+	      err = transit_state_bkref (mctx, new_dest_nodes);
+	      if (BE (err != REG_NOERROR, 0))
+		goto free_return;
+	    }
+	}
+    }
+  err = REG_NOERROR;
+ free_return:
+  return err;
+}
+
+/* Enumerate all the candidates which the backreference BKREF_NODE can match
+   at BKREF_STR_IDX, and register them by match_ctx_add_entry().
+   Note that we might collect inappropriate candidates here.
+   However, the cost of checking them strictly here is too high, then we
+   delay these checking for prune_impossible_nodes().  */
+
+static reg_errcode_t
+internal_function
+get_subexp (re_match_context_t *mctx, int bkref_node, int bkref_str_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int subexp_num, sub_top_idx;
+  const char *buf = (const char *) re_string_get_buffer (&mctx->input);
+  /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX.  */
+  int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx);
+  if (cache_idx != -1)
+    {
+      const struct re_backref_cache_entry *entry
+	= mctx->bkref_ents + cache_idx;
+      do
+        if (entry->node == bkref_node)
+	  return REG_NOERROR; /* We already checked it.  */
+      while (entry++->more);
+    }
+
+  subexp_num = dfa->nodes[bkref_node].opr.idx;
+
+  /* For each sub expression  */
+  for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx)
+    {
+      reg_errcode_t err;
+      re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx];
+      re_sub_match_last_t *sub_last;
+      int sub_last_idx, sl_str, bkref_str_off;
+
+      if (dfa->nodes[sub_top->node].opr.idx != subexp_num)
+	continue; /* It isn't related.  */
+
+      sl_str = sub_top->str_idx;
+      bkref_str_off = bkref_str_idx;
+      /* At first, check the last node of sub expressions we already
+	 evaluated.  */
+      for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx)
+	{
+	  int sl_str_diff;
+	  sub_last = sub_top->lasts[sub_last_idx];
+	  sl_str_diff = sub_last->str_idx - sl_str;
+	  /* The matched string by the sub expression match with the substring
+	     at the back reference?  */
+	  if (sl_str_diff > 0)
+	    {
+	      if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0))
+		{
+		  /* Not enough chars for a successful match.  */
+		  if (bkref_str_off + sl_str_diff > mctx->input.len)
+		    break;
+
+		  err = clean_state_log_if_needed (mctx,
+						   bkref_str_off
+						   + sl_str_diff);
+		  if (BE (err != REG_NOERROR, 0))
+		    return err;
+		  buf = (const char *) re_string_get_buffer (&mctx->input);
+		}
+	      if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0)
+		/* We don't need to search this sub expression any more.  */
+		break;
+	    }
+	  bkref_str_off += sl_str_diff;
+	  sl_str += sl_str_diff;
+	  err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+				bkref_str_idx);
+
+	  /* Reload buf, since the preceding call might have reallocated
+	     the buffer.  */
+	  buf = (const char *) re_string_get_buffer (&mctx->input);
+
+	  if (err == REG_NOMATCH)
+	    continue;
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+
+      if (sub_last_idx < sub_top->nlasts)
+	continue;
+      if (sub_last_idx > 0)
+	++sl_str;
+      /* Then, search for the other last nodes of the sub expression.  */
+      for (; sl_str <= bkref_str_idx; ++sl_str)
+	{
+	  int cls_node, sl_str_off;
+	  const re_node_set *nodes;
+	  sl_str_off = sl_str - sub_top->str_idx;
+	  /* The matched string by the sub expression match with the substring
+	     at the back reference?  */
+	  if (sl_str_off > 0)
+	    {
+	      if (BE (bkref_str_off >= mctx->input.valid_len, 0))
+		{
+		  /* If we are at the end of the input, we cannot match.  */
+		  if (bkref_str_off >= mctx->input.len)
+		    break;
+
+		  err = extend_buffers (mctx);
+		  if (BE (err != REG_NOERROR, 0))
+		    return err;
+
+		  buf = (const char *) re_string_get_buffer (&mctx->input);
+		}
+	      if (buf [bkref_str_off++] != buf[sl_str - 1])
+		break; /* We don't need to search this sub expression
+			  any more.  */
+	    }
+	  if (mctx->state_log[sl_str] == NULL)
+	    continue;
+	  /* Does this state have a ')' of the sub expression?  */
+	  nodes = &mctx->state_log[sl_str]->nodes;
+	  cls_node = find_subexp_node (dfa, nodes, subexp_num,
+				       OP_CLOSE_SUBEXP);
+	  if (cls_node == -1)
+	    continue; /* No.  */
+	  if (sub_top->path == NULL)
+	    {
+	      sub_top->path = calloc (sizeof (state_array_t),
+				      sl_str - sub_top->str_idx + 1);
+	      if (sub_top->path == NULL)
+		return REG_ESPACE;
+	    }
+	  /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node
+	     in the current context?  */
+	  err = check_arrival (mctx, sub_top->path, sub_top->node,
+			       sub_top->str_idx, cls_node, sl_str,
+			       OP_CLOSE_SUBEXP);
+	  if (err == REG_NOMATCH)
+	      continue;
+	  if (BE (err != REG_NOERROR, 0))
+	      return err;
+	  sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str);
+	  if (BE (sub_last == NULL, 0))
+	    return REG_ESPACE;
+	  err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+				bkref_str_idx);
+	  if (err == REG_NOMATCH)
+	    continue;
+	}
+    }
+  return REG_NOERROR;
+}
+
+/* Helper functions for get_subexp().  */
+
+/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR.
+   If it can arrive, register the sub expression expressed with SUB_TOP
+   and SUB_LAST.  */
+
+static reg_errcode_t
+internal_function
+get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top,
+		re_sub_match_last_t *sub_last, int bkref_node, int bkref_str)
+{
+  reg_errcode_t err;
+  int to_idx;
+  /* Can the subexpression arrive the back reference?  */
+  err = check_arrival (mctx, &sub_last->path, sub_last->node,
+		       sub_last->str_idx, bkref_node, bkref_str,
+		       OP_OPEN_SUBEXP);
+  if (err != REG_NOERROR)
+    return err;
+  err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx,
+			     sub_last->str_idx);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx;
+  return clean_state_log_if_needed (mctx, to_idx);
+}
+
+/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX.
+   Search '(' if FL_OPEN, or search ')' otherwise.
+   TODO: This function isn't efficient...
+	 Because there might be more than one nodes whose types are
+	 OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+	 nodes.
+	 E.g. RE: (a){2}  */
+
+static int
+internal_function
+find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+		  int subexp_idx, int type)
+{
+  int cls_idx;
+  for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx)
+    {
+      int cls_node = nodes->elems[cls_idx];
+      const re_token_t *node = dfa->nodes + cls_node;
+      if (node->type == type
+	  && node->opr.idx == subexp_idx)
+	return cls_node;
+    }
+  return -1;
+}
+
+/* Check whether the node TOP_NODE at TOP_STR can arrive to the node
+   LAST_NODE at LAST_STR.  We record the path onto PATH since it will be
+   heavily reused.
+   Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise.  */
+
+static reg_errcode_t
+internal_function
+check_arrival (re_match_context_t *mctx, state_array_t *path, int top_node,
+	       int top_str, int last_node, int last_str, int type)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err = REG_NOERROR;
+  int subexp_num, backup_cur_idx, str_idx, null_cnt;
+  re_dfastate_t *cur_state = NULL;
+  re_node_set *cur_nodes, next_nodes;
+  re_dfastate_t **backup_state_log;
+  unsigned int context;
+
+  subexp_num = dfa->nodes[top_node].opr.idx;
+  /* Extend the buffer if we need.  */
+  if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0))
+    {
+      re_dfastate_t **new_array;
+      int old_alloc = path->alloc;
+      path->alloc += last_str + mctx->max_mb_elem_len + 1;
+      new_array = re_realloc (path->array, re_dfastate_t *, path->alloc);
+      if (BE (new_array == NULL, 0))
+	{
+	  path->alloc = old_alloc;
+	  return REG_ESPACE;
+	}
+      path->array = new_array;
+      memset (new_array + old_alloc, '\0',
+	      sizeof (re_dfastate_t *) * (path->alloc - old_alloc));
+    }
+
+  str_idx = path->next_idx ?: top_str;
+
+  /* Temporary modify MCTX.  */
+  backup_state_log = mctx->state_log;
+  backup_cur_idx = mctx->input.cur_idx;
+  mctx->state_log = path->array;
+  mctx->input.cur_idx = str_idx;
+
+  /* Setup initial node set.  */
+  context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+  if (str_idx == top_str)
+    {
+      err = re_node_set_init_1 (&next_nodes, top_node);
+      if (BE (err != REG_NOERROR, 0))
+	return err;
+      err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+      if (BE (err != REG_NOERROR, 0))
+	{
+	  re_node_set_free (&next_nodes);
+	  return err;
+	}
+    }
+  else
+    {
+      cur_state = mctx->state_log[str_idx];
+      if (cur_state && cur_state->has_backref)
+	{
+	  err = re_node_set_init_copy (&next_nodes, &cur_state->nodes);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+      else
+	re_node_set_init_empty (&next_nodes);
+    }
+  if (str_idx == top_str || (cur_state && cur_state->has_backref))
+    {
+      if (next_nodes.nelem)
+	{
+	  err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+				    subexp_num, type);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return err;
+	    }
+	}
+      cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+      if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+	{
+	  re_node_set_free (&next_nodes);
+	  return err;
+	}
+      mctx->state_log[str_idx] = cur_state;
+    }
+
+  for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;)
+    {
+      re_node_set_empty (&next_nodes);
+      if (mctx->state_log[str_idx + 1])
+	{
+	  err = re_node_set_merge (&next_nodes,
+				   &mctx->state_log[str_idx + 1]->nodes);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return err;
+	    }
+	}
+      if (cur_state)
+	{
+	  err = check_arrival_add_next_nodes (mctx, str_idx,
+					      &cur_state->non_eps_nodes,
+					      &next_nodes);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return err;
+	    }
+	}
+      ++str_idx;
+      if (next_nodes.nelem)
+	{
+	  err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return err;
+	    }
+	  err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+				    subexp_num, type);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&next_nodes);
+	      return err;
+	    }
+	}
+      context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+      cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+      if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+	{
+	  re_node_set_free (&next_nodes);
+	  return err;
+	}
+      mctx->state_log[str_idx] = cur_state;
+      null_cnt = cur_state == NULL ? null_cnt + 1 : 0;
+    }
+  re_node_set_free (&next_nodes);
+  cur_nodes = (mctx->state_log[last_str] == NULL ? NULL
+	       : &mctx->state_log[last_str]->nodes);
+  path->next_idx = str_idx;
+
+  /* Fix MCTX.  */
+  mctx->state_log = backup_state_log;
+  mctx->input.cur_idx = backup_cur_idx;
+
+  /* Then check the current node set has the node LAST_NODE.  */
+  if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node))
+    return REG_NOERROR;
+
+  return REG_NOMATCH;
+}
+
+/* Helper functions for check_arrival.  */
+
+/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them
+   to NEXT_NODES.
+   TODO: This function is similar to the functions transit_state*(),
+	 however this function has many additional works.
+	 Can't we unify them?  */
+
+static reg_errcode_t
+internal_function
+check_arrival_add_next_nodes (re_match_context_t *mctx, int str_idx,
+			      re_node_set *cur_nodes, re_node_set *next_nodes)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  int result;
+  int cur_idx;
+  reg_errcode_t err = REG_NOERROR;
+  re_node_set union_set;
+  re_node_set_init_empty (&union_set);
+  for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx)
+    {
+      int naccepted = 0;
+      int cur_node = cur_nodes->elems[cur_idx];
+#ifdef DEBUG
+      re_token_type_t type = dfa->nodes[cur_node].type;
+      assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+      /* If the node may accept `multi byte'.  */
+      if (dfa->nodes[cur_node].accept_mb)
+	{
+	  naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input,
+					       str_idx);
+	  if (naccepted > 1)
+	    {
+	      re_dfastate_t *dest_state;
+	      int next_node = dfa->nexts[cur_node];
+	      int next_idx = str_idx + naccepted;
+	      dest_state = mctx->state_log[next_idx];
+	      re_node_set_empty (&union_set);
+	      if (dest_state)
+		{
+		  err = re_node_set_merge (&union_set, &dest_state->nodes);
+		  if (BE (err != REG_NOERROR, 0))
+		    {
+		      re_node_set_free (&union_set);
+		      return err;
+		    }
+		}
+	      result = re_node_set_insert (&union_set, next_node);
+	      if (BE (result < 0, 0))
+		{
+		  re_node_set_free (&union_set);
+		  return REG_ESPACE;
+		}
+	      mctx->state_log[next_idx] = re_acquire_state (&err, dfa,
+							    &union_set);
+	      if (BE (mctx->state_log[next_idx] == NULL
+		      && err != REG_NOERROR, 0))
+		{
+		  re_node_set_free (&union_set);
+		  return err;
+		}
+	    }
+	}
+#endif /* RE_ENABLE_I18N */
+      if (naccepted
+	  || check_node_accept (mctx, dfa->nodes + cur_node, str_idx))
+	{
+	  result = re_node_set_insert (next_nodes, dfa->nexts[cur_node]);
+	  if (BE (result < 0, 0))
+	    {
+	      re_node_set_free (&union_set);
+	      return REG_ESPACE;
+	    }
+	}
+    }
+  re_node_set_free (&union_set);
+  return REG_NOERROR;
+}
+
+/* For all the nodes in CUR_NODES, add the epsilon closures of them to
+   CUR_NODES, however exclude the nodes which are:
+    - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN.
+    - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN.
+*/
+
+static reg_errcode_t
+internal_function
+check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes,
+			  int ex_subexp, int type)
+{
+  reg_errcode_t err;
+  int idx, outside_node;
+  re_node_set new_nodes;
+#ifdef DEBUG
+  assert (cur_nodes->nelem);
+#endif
+  err = re_node_set_alloc (&new_nodes, cur_nodes->nelem);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  /* Create a new node set NEW_NODES with the nodes which are epsilon
+     closures of the node in CUR_NODES.  */
+
+  for (idx = 0; idx < cur_nodes->nelem; ++idx)
+    {
+      int cur_node = cur_nodes->elems[idx];
+      const re_node_set *eclosure = dfa->eclosures + cur_node;
+      outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type);
+      if (outside_node == -1)
+	{
+	  /* There are no problematic nodes, just merge them.  */
+	  err = re_node_set_merge (&new_nodes, eclosure);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&new_nodes);
+	      return err;
+	    }
+	}
+      else
+	{
+	  /* There are problematic nodes, re-calculate incrementally.  */
+	  err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node,
+					      ex_subexp, type);
+	  if (BE (err != REG_NOERROR, 0))
+	    {
+	      re_node_set_free (&new_nodes);
+	      return err;
+	    }
+	}
+    }
+  re_node_set_free (cur_nodes);
+  *cur_nodes = new_nodes;
+  return REG_NOERROR;
+}
+
+/* Helper function for check_arrival_expand_ecl.
+   Check incrementally the epsilon closure of TARGET, and if it isn't
+   problematic append it to DST_NODES.  */
+
+static reg_errcode_t
+internal_function
+check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes,
+			      int target, int ex_subexp, int type)
+{
+  int cur_node;
+  for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);)
+    {
+      int err;
+
+      if (dfa->nodes[cur_node].type == type
+	  && dfa->nodes[cur_node].opr.idx == ex_subexp)
+	{
+	  if (type == OP_CLOSE_SUBEXP)
+	    {
+	      err = re_node_set_insert (dst_nodes, cur_node);
+	      if (BE (err == -1, 0))
+		return REG_ESPACE;
+	    }
+	  break;
+	}
+      err = re_node_set_insert (dst_nodes, cur_node);
+      if (BE (err == -1, 0))
+	return REG_ESPACE;
+      if (dfa->edests[cur_node].nelem == 0)
+	break;
+      if (dfa->edests[cur_node].nelem == 2)
+	{
+	  err = check_arrival_expand_ecl_sub (dfa, dst_nodes,
+					      dfa->edests[cur_node].elems[1],
+					      ex_subexp, type);
+	  if (BE (err != REG_NOERROR, 0))
+	    return err;
+	}
+      cur_node = dfa->edests[cur_node].elems[0];
+    }
+  return REG_NOERROR;
+}
+
+
+/* For all the back references in the current state, calculate the
+   destination of the back references by the appropriate entry
+   in MCTX->BKREF_ENTS.  */
+
+static reg_errcode_t
+internal_function
+expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes,
+		    int cur_str, int subexp_num, int type)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err;
+  int cache_idx_start = search_cur_bkref_entry (mctx, cur_str);
+  struct re_backref_cache_entry *ent;
+
+  if (cache_idx_start == -1)
+    return REG_NOERROR;
+
+ restart:
+  ent = mctx->bkref_ents + cache_idx_start;
+  do
+    {
+      int to_idx, next_node;
+
+      /* Is this entry ENT is appropriate?  */
+      if (!re_node_set_contains (cur_nodes, ent->node))
+	continue; /* No.  */
+
+      to_idx = cur_str + ent->subexp_to - ent->subexp_from;
+      /* Calculate the destination of the back reference, and append it
+	 to MCTX->STATE_LOG.  */
+      if (to_idx == cur_str)
+	{
+	  /* The backreference did epsilon transit, we must re-check all the
+	     node in the current state.  */
+	  re_node_set new_dests;
+	  reg_errcode_t err2, err3;
+	  next_node = dfa->edests[ent->node].elems[0];
+	  if (re_node_set_contains (cur_nodes, next_node))
+	    continue;
+	  err = re_node_set_init_1 (&new_dests, next_node);
+	  err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type);
+	  err3 = re_node_set_merge (cur_nodes, &new_dests);
+	  re_node_set_free (&new_dests);
+	  if (BE (err != REG_NOERROR || err2 != REG_NOERROR
+		  || err3 != REG_NOERROR, 0))
+	    {
+	      err = (err != REG_NOERROR ? err
+		     : (err2 != REG_NOERROR ? err2 : err3));
+	      return err;
+	    }
+	  /* TODO: It is still inefficient...  */
+	  goto restart;
+	}
+      else
+	{
+	  re_node_set union_set;
+	  next_node = dfa->nexts[ent->node];
+	  if (mctx->state_log[to_idx])
+	    {
+	      int ret;
+	      if (re_node_set_contains (&mctx->state_log[to_idx]->nodes,
+					next_node))
+		continue;
+	      err = re_node_set_init_copy (&union_set,
+					   &mctx->state_log[to_idx]->nodes);
+	      ret = re_node_set_insert (&union_set, next_node);
+	      if (BE (err != REG_NOERROR || ret < 0, 0))
+		{
+		  re_node_set_free (&union_set);
+		  err = err != REG_NOERROR ? err : REG_ESPACE;
+		  return err;
+		}
+	    }
+	  else
+	    {
+	      err = re_node_set_init_1 (&union_set, next_node);
+	      if (BE (err != REG_NOERROR, 0))
+		return err;
+	    }
+	  mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set);
+	  re_node_set_free (&union_set);
+	  if (BE (mctx->state_log[to_idx] == NULL
+		  && err != REG_NOERROR, 0))
+	    return err;
+	}
+    }
+  while (ent++->more);
+  return REG_NOERROR;
+}
+
+/* Build transition table for the state.
+   Return 1 if succeeded, otherwise return NULL.  */
+
+static int
+internal_function
+build_trtable (const re_dfa_t *dfa, re_dfastate_t *state)
+{
+  reg_errcode_t err;
+  int i, j, ch, need_word_trtable = 0;
+  bitset_word_t elem, mask;
+  bool dests_node_malloced = false;
+  bool dest_states_malloced = false;
+  int ndests; /* Number of the destination states from `state'.  */
+  re_dfastate_t **trtable;
+  re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl;
+  re_node_set follows, *dests_node;
+  bitset_t *dests_ch;
+  bitset_t acceptable;
+
+  struct dests_alloc
+  {
+    re_node_set dests_node[SBC_MAX];
+    bitset_t dests_ch[SBC_MAX];
+  } *dests_alloc;
+
+  /* We build DFA states which corresponds to the destination nodes
+     from `state'.  `dests_node[i]' represents the nodes which i-th
+     destination state contains, and `dests_ch[i]' represents the
+     characters which i-th destination state accepts.  */
+  if (__libc_use_alloca (sizeof (struct dests_alloc)))
+    dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc));
+  else
+    {
+      dests_alloc = re_malloc (struct dests_alloc, 1);
+      if (BE (dests_alloc == NULL, 0))
+	return 0;
+      dests_node_malloced = true;
+    }
+  dests_node = dests_alloc->dests_node;
+  dests_ch = dests_alloc->dests_ch;
+
+  /* Initialize transiton table.  */
+  state->word_trtable = state->trtable = NULL;
+
+  /* At first, group all nodes belonging to `state' into several
+     destinations.  */
+  ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch);
+  if (BE (ndests <= 0, 0))
+    {
+      if (dests_node_malloced)
+	free (dests_alloc);
+      /* Return 0 in case of an error, 1 otherwise.  */
+      if (ndests == 0)
+	{
+	  state->trtable = (re_dfastate_t **)
+	    calloc (sizeof (re_dfastate_t *), SBC_MAX);
+	  return 1;
+	}
+      return 0;
+    }
+
+  err = re_node_set_alloc (&follows, ndests + 1);
+  if (BE (err != REG_NOERROR, 0))
+    goto out_free;
+
+  if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX
+			 + ndests * 3 * sizeof (re_dfastate_t *)))
+    dest_states = (re_dfastate_t **)
+      alloca (ndests * 3 * sizeof (re_dfastate_t *));
+  else
+    {
+      dest_states = (re_dfastate_t **)
+	malloc (ndests * 3 * sizeof (re_dfastate_t *));
+      if (BE (dest_states == NULL, 0))
+	{
+out_free:
+	  if (dest_states_malloced)
+	    free (dest_states);
+	  re_node_set_free (&follows);
+	  for (i = 0; i < ndests; ++i)
+	    re_node_set_free (dests_node + i);
+	  if (dests_node_malloced)
+	    free (dests_alloc);
+	  return 0;
+	}
+      dest_states_malloced = true;
+    }
+  dest_states_word = dest_states + ndests;
+  dest_states_nl = dest_states_word + ndests;
+  bitset_empty (acceptable);
+
+  /* Then build the states for all destinations.  */
+  for (i = 0; i < ndests; ++i)
+    {
+      int next_node;
+      re_node_set_empty (&follows);
+      /* Merge the follows of this destination states.  */
+      for (j = 0; j < dests_node[i].nelem; ++j)
+	{
+	  next_node = dfa->nexts[dests_node[i].elems[j]];
+	  if (next_node != -1)
+	    {
+	      err = re_node_set_merge (&follows, dfa->eclosures + next_node);
+	      if (BE (err != REG_NOERROR, 0))
+		goto out_free;
+	    }
+	}
+      dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0);
+      if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0))
+	goto out_free;
+      /* If the new state has context constraint,
+	 build appropriate states for these contexts.  */
+      if (dest_states[i]->has_constraint)
+	{
+	  dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows,
+							  CONTEXT_WORD);
+	  if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0))
+	    goto out_free;
+
+	  if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1)
+	    need_word_trtable = 1;
+
+	  dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows,
+							CONTEXT_NEWLINE);
+	  if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0))
+	    goto out_free;
+ 	}
+      else
+	{
+	  dest_states_word[i] = dest_states[i];
+	  dest_states_nl[i] = dest_states[i];
+	}
+      bitset_merge (acceptable, dests_ch[i]);
+    }
+
+  if (!BE (need_word_trtable, 0))
+    {
+      /* We don't care about whether the following character is a word
+	 character, or we are in a single-byte character set so we can
+	 discern by looking at the character code: allocate a
+	 256-entry transition table.  */
+      trtable = state->trtable =
+	(re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+      if (BE (trtable == NULL, 0))
+	goto out_free;
+
+      /* For all characters ch...:  */
+      for (i = 0; i < BITSET_WORDS; ++i)
+	for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+	     elem;
+	     mask <<= 1, elem >>= 1, ++ch)
+	  if (BE (elem & 1, 0))
+	    {
+	      /* There must be exactly one destination which accepts
+		 character ch.  See group_nodes_into_DFAstates.  */
+	      for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+		;
+
+	      /* j-th destination accepts the word character ch.  */
+	      if (dfa->word_char[i] & mask)
+		trtable[ch] = dest_states_word[j];
+	      else
+		trtable[ch] = dest_states[j];
+	    }
+    }
+  else
+    {
+      /* We care about whether the following character is a word
+	 character, and we are in a multi-byte character set: discern
+	 by looking at the character code: build two 256-entry
+	 transition tables, one starting at trtable[0] and one
+	 starting at trtable[SBC_MAX].  */
+      trtable = state->word_trtable =
+	(re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX);
+      if (BE (trtable == NULL, 0))
+	goto out_free;
+
+      /* For all characters ch...:  */
+      for (i = 0; i < BITSET_WORDS; ++i)
+	for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+	     elem;
+	     mask <<= 1, elem >>= 1, ++ch)
+	  if (BE (elem & 1, 0))
+	    {
+	      /* There must be exactly one destination which accepts
+		 character ch.  See group_nodes_into_DFAstates.  */
+	      for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+		;
+
+	      /* j-th destination accepts the word character ch.  */
+	      trtable[ch] = dest_states[j];
+	      trtable[ch + SBC_MAX] = dest_states_word[j];
+	    }
+    }
+
+  /* new line */
+  if (bitset_contain (acceptable, NEWLINE_CHAR))
+    {
+      /* The current state accepts newline character.  */
+      for (j = 0; j < ndests; ++j)
+	if (bitset_contain (dests_ch[j], NEWLINE_CHAR))
+	  {
+	    /* k-th destination accepts newline character.  */
+	    trtable[NEWLINE_CHAR] = dest_states_nl[j];
+	    if (need_word_trtable)
+	      trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j];
+	    /* There must be only one destination which accepts
+	       newline.  See group_nodes_into_DFAstates.  */
+	    break;
+	  }
+    }
+
+  if (dest_states_malloced)
+    free (dest_states);
+
+  re_node_set_free (&follows);
+  for (i = 0; i < ndests; ++i)
+    re_node_set_free (dests_node + i);
+
+  if (dests_node_malloced)
+    free (dests_alloc);
+
+  return 1;
+}
+
+/* Group all nodes belonging to STATE into several destinations.
+   Then for all destinations, set the nodes belonging to the destination
+   to DESTS_NODE[i] and set the characters accepted by the destination
+   to DEST_CH[i].  This function return the number of destinations.  */
+
+static int
+internal_function
+group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state,
+			    re_node_set *dests_node, bitset_t *dests_ch)
+{
+  reg_errcode_t err;
+  int result;
+  int i, j, k;
+  int ndests; /* Number of the destinations from `state'.  */
+  bitset_t accepts; /* Characters a node can accept.  */
+  const re_node_set *cur_nodes = &state->nodes;
+  bitset_empty (accepts);
+  ndests = 0;
+
+  /* For all the nodes belonging to `state',  */
+  for (i = 0; i < cur_nodes->nelem; ++i)
+    {
+      re_token_t *node = &dfa->nodes[cur_nodes->elems[i]];
+      re_token_type_t type = node->type;
+      unsigned int constraint = node->constraint;
+
+      /* Enumerate all single byte character this node can accept.  */
+      if (type == CHARACTER)
+	bitset_set (accepts, node->opr.c);
+      else if (type == SIMPLE_BRACKET)
+	{
+	  bitset_merge (accepts, node->opr.sbcset);
+	}
+      else if (type == OP_PERIOD)
+	{
+#ifdef RE_ENABLE_I18N
+	  if (dfa->mb_cur_max > 1)
+	    bitset_merge (accepts, dfa->sb_char);
+	  else
+#endif
+	    bitset_set_all (accepts);
+	  if (!(dfa->syntax & RE_DOT_NEWLINE))
+	    bitset_clear (accepts, '\n');
+	  if (dfa->syntax & RE_DOT_NOT_NULL)
+	    bitset_clear (accepts, '\0');
+	}
+#ifdef RE_ENABLE_I18N
+      else if (type == OP_UTF8_PERIOD)
+        {
+	  memset (accepts, '\xff', sizeof (bitset_t) / 2);
+	  if (!(dfa->syntax & RE_DOT_NEWLINE))
+	    bitset_clear (accepts, '\n');
+	  if (dfa->syntax & RE_DOT_NOT_NULL)
+	    bitset_clear (accepts, '\0');
+        }
+#endif
+      else
+	continue;
+
+      /* Check the `accepts' and sift the characters which are not
+	 match it the context.  */
+      if (constraint)
+	{
+	  if (constraint & NEXT_NEWLINE_CONSTRAINT)
+	    {
+	      bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR);
+	      bitset_empty (accepts);
+	      if (accepts_newline)
+		bitset_set (accepts, NEWLINE_CHAR);
+	      else
+		continue;
+	    }
+	  if (constraint & NEXT_ENDBUF_CONSTRAINT)
+	    {
+	      bitset_empty (accepts);
+	      continue;
+	    }
+
+	  if (constraint & NEXT_WORD_CONSTRAINT)
+	    {
+	      bitset_word_t any_set = 0;
+	      if (type == CHARACTER && !node->word_char)
+		{
+		  bitset_empty (accepts);
+		  continue;
+		}
+#ifdef RE_ENABLE_I18N
+	      if (dfa->mb_cur_max > 1)
+		for (j = 0; j < BITSET_WORDS; ++j)
+		  any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j]));
+	      else
+#endif
+		for (j = 0; j < BITSET_WORDS; ++j)
+		  any_set |= (accepts[j] &= dfa->word_char[j]);
+	      if (!any_set)
+		continue;
+	    }
+	  if (constraint & NEXT_NOTWORD_CONSTRAINT)
+	    {
+	      bitset_word_t any_set = 0;
+	      if (type == CHARACTER && node->word_char)
+		{
+		  bitset_empty (accepts);
+		  continue;
+		}
+#ifdef RE_ENABLE_I18N
+	      if (dfa->mb_cur_max > 1)
+		for (j = 0; j < BITSET_WORDS; ++j)
+		  any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j]));
+	      else
+#endif
+		for (j = 0; j < BITSET_WORDS; ++j)
+		  any_set |= (accepts[j] &= ~dfa->word_char[j]);
+	      if (!any_set)
+		continue;
+	    }
+	}
+
+      /* Then divide `accepts' into DFA states, or create a new
+	 state.  Above, we make sure that accepts is not empty.  */
+      for (j = 0; j < ndests; ++j)
+	{
+	  bitset_t intersec; /* Intersection sets, see below.  */
+	  bitset_t remains;
+	  /* Flags, see below.  */
+	  bitset_word_t has_intersec, not_subset, not_consumed;
+
+	  /* Optimization, skip if this state doesn't accept the character.  */
+	  if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c))
+	    continue;
+
+	  /* Enumerate the intersection set of this state and `accepts'.  */
+	  has_intersec = 0;
+	  for (k = 0; k < BITSET_WORDS; ++k)
+	    has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k];
+	  /* And skip if the intersection set is empty.  */
+	  if (!has_intersec)
+	    continue;
+
+	  /* Then check if this state is a subset of `accepts'.  */
+	  not_subset = not_consumed = 0;
+	  for (k = 0; k < BITSET_WORDS; ++k)
+	    {
+	      not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k];
+	      not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k];
+	    }
+
+	  /* If this state isn't a subset of `accepts', create a
+	     new group state, which has the `remains'. */
+	  if (not_subset)
+	    {
+	      bitset_copy (dests_ch[ndests], remains);
+	      bitset_copy (dests_ch[j], intersec);
+	      err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]);
+	      if (BE (err != REG_NOERROR, 0))
+		goto error_return;
+	      ++ndests;
+	    }
+
+	  /* Put the position in the current group. */
+	  result = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
+	  if (BE (result < 0, 0))
+	    goto error_return;
+
+	  /* If all characters are consumed, go to next node. */
+	  if (!not_consumed)
+	    break;
+	}
+      /* Some characters remain, create a new group. */
+      if (j == ndests)
+	{
+	  bitset_copy (dests_ch[ndests], accepts);
+	  err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]);
+	  if (BE (err != REG_NOERROR, 0))
+	    goto error_return;
+	  ++ndests;
+	  bitset_empty (accepts);
+	}
+    }
+  return ndests;
+ error_return:
+  for (j = 0; j < ndests; ++j)
+    re_node_set_free (dests_node + j);
+  return -1;
+}
+
+#ifdef RE_ENABLE_I18N
+/* Check how many bytes the node `dfa->nodes[node_idx]' accepts.
+   Return the number of the bytes the node accepts.
+   STR_IDX is the current index of the input string.
+
+   This function handles the nodes which can accept one character, or
+   one collating element like '.', '[a-z]', opposite to the other nodes
+   can only accept one byte.  */
+
+static int
+internal_function
+check_node_accept_bytes (const re_dfa_t *dfa, int node_idx,
+			 const re_string_t *input, int str_idx)
+{
+  const re_token_t *node = dfa->nodes + node_idx;
+  int char_len, elem_len;
+  int i;
+
+  if (BE (node->type == OP_UTF8_PERIOD, 0))
+    {
+      unsigned char c = re_string_byte_at (input, str_idx), d;
+      if (BE (c < 0xc2, 1))
+	return 0;
+
+      if (str_idx + 2 > input->len)
+	return 0;
+
+      d = re_string_byte_at (input, str_idx + 1);
+      if (c < 0xe0)
+	return (d < 0x80 || d > 0xbf) ? 0 : 2;
+      else if (c < 0xf0)
+	{
+	  char_len = 3;
+	  if (c == 0xe0 && d < 0xa0)
+	    return 0;
+	}
+      else if (c < 0xf8)
+	{
+	  char_len = 4;
+	  if (c == 0xf0 && d < 0x90)
+	    return 0;
+	}
+      else if (c < 0xfc)
+	{
+	  char_len = 5;
+	  if (c == 0xf8 && d < 0x88)
+	    return 0;
+	}
+      else if (c < 0xfe)
+	{
+	  char_len = 6;
+	  if (c == 0xfc && d < 0x84)
+	    return 0;
+	}
+      else
+	return 0;
+
+      if (str_idx + char_len > input->len)
+	return 0;
+
+      for (i = 1; i < char_len; ++i)
+	{
+	  d = re_string_byte_at (input, str_idx + i);
+	  if (d < 0x80 || d > 0xbf)
+	    return 0;
+	}
+      return char_len;
+    }
+
+  char_len = re_string_char_size_at (input, str_idx);
+  if (node->type == OP_PERIOD)
+    {
+      if (char_len <= 1)
+        return 0;
+      /* FIXME: I don't think this if is needed, as both '\n'
+	 and '\0' are char_len == 1.  */
+      /* '.' accepts any one character except the following two cases.  */
+      if ((!(dfa->syntax & RE_DOT_NEWLINE) &&
+	   re_string_byte_at (input, str_idx) == '\n') ||
+	  ((dfa->syntax & RE_DOT_NOT_NULL) &&
+	   re_string_byte_at (input, str_idx) == '\0'))
+	return 0;
+      return char_len;
+    }
+
+  elem_len = re_string_elem_size_at (input, str_idx);
+  if ((elem_len <= 1 && char_len <= 1) || char_len == 0)
+    return 0;
+
+  if (node->type == COMPLEX_BRACKET)
+    {
+      const re_charset_t *cset = node->opr.mbcset;
+# ifdef _LIBC
+      const unsigned char *pin
+	= ((const unsigned char *) re_string_get_buffer (input) + str_idx);
+      int j;
+      uint32_t nrules;
+# endif /* _LIBC */
+      int match_len = 0;
+      wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars)
+		    ? re_string_wchar_at (input, str_idx) : 0);
+
+      /* match with multibyte character?  */
+      for (i = 0; i < cset->nmbchars; ++i)
+	if (wc == cset->mbchars[i])
+	  {
+	    match_len = char_len;
+	    goto check_node_accept_bytes_match;
+	  }
+      /* match with character_class?  */
+      for (i = 0; i < cset->nchar_classes; ++i)
+	{
+	  wctype_t wt = cset->char_classes[i];
+	  if (__iswctype (wc, wt))
+	    {
+	      match_len = char_len;
+	      goto check_node_accept_bytes_match;
+	    }
+	}
+
+# ifdef _LIBC
+      nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+      if (nrules != 0)
+	{
+	  unsigned int in_collseq = 0;
+	  const int32_t *table, *indirect;
+	  const unsigned char *weights, *extra;
+	  const char *collseqwc;
+	  /* This #include defines a local function!  */
+#  include <locale/weight.h>
+
+	  /* match with collating_symbol?  */
+	  if (cset->ncoll_syms)
+	    extra = (const unsigned char *)
+	      _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+	  for (i = 0; i < cset->ncoll_syms; ++i)
+	    {
+	      const unsigned char *coll_sym = extra + cset->coll_syms[i];
+	      /* Compare the length of input collating element and
+		 the length of current collating element.  */
+	      if (*coll_sym != elem_len)
+		continue;
+	      /* Compare each bytes.  */
+	      for (j = 0; j < *coll_sym; j++)
+		if (pin[j] != coll_sym[1 + j])
+		  break;
+	      if (j == *coll_sym)
+		{
+		  /* Match if every bytes is equal.  */
+		  match_len = j;
+		  goto check_node_accept_bytes_match;
+		}
+	    }
+
+	  if (cset->nranges)
+	    {
+	      if (elem_len <= char_len)
+		{
+		  collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+		  in_collseq = __collseq_table_lookup (collseqwc, wc);
+		}
+	      else
+		in_collseq = find_collation_sequence_value (pin, elem_len);
+	    }
+	  /* match with range expression?  */
+	  for (i = 0; i < cset->nranges; ++i)
+	    if (cset->range_starts[i] <= in_collseq
+		&& in_collseq <= cset->range_ends[i])
+	      {
+		match_len = elem_len;
+		goto check_node_accept_bytes_match;
+	      }
+
+	  /* match with equivalence_class?  */
+	  if (cset->nequiv_classes)
+	    {
+	      const unsigned char *cp = pin;
+	      table = (const int32_t *)
+		_NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+	      weights = (const unsigned char *)
+		_NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
+	      extra = (const unsigned char *)
+		_NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+	      indirect = (const int32_t *)
+		_NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
+	      int32_t idx = findidx (&cp);
+	      if (idx > 0)
+		for (i = 0; i < cset->nequiv_classes; ++i)
+		  {
+		    int32_t equiv_class_idx = cset->equiv_classes[i];
+		    size_t weight_len = weights[idx & 0xffffff];
+		    if (weight_len == weights[equiv_class_idx & 0xffffff]
+			&& (idx >> 24) == (equiv_class_idx >> 24))
+		      {
+			int cnt = 0;
+
+			idx &= 0xffffff;
+			equiv_class_idx &= 0xffffff;
+
+			while (cnt <= weight_len
+			       && (weights[equiv_class_idx + 1 + cnt]
+				   == weights[idx + 1 + cnt]))
+			  ++cnt;
+			if (cnt > weight_len)
+			  {
+			    match_len = elem_len;
+			    goto check_node_accept_bytes_match;
+			  }
+		      }
+		  }
+	    }
+	}
+      else
+# endif /* _LIBC */
+	{
+	  /* match with range expression?  */
+#if __GNUC__ >= 2
+	  wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'};
+#else
+	  wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+	  cmp_buf[2] = wc;
+#endif
+	  for (i = 0; i < cset->nranges; ++i)
+	    {
+	      cmp_buf[0] = cset->range_starts[i];
+	      cmp_buf[4] = cset->range_ends[i];
+	      if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+		  && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+		{
+		  match_len = char_len;
+		  goto check_node_accept_bytes_match;
+		}
+	    }
+	}
+    check_node_accept_bytes_match:
+      if (!cset->non_match)
+	return match_len;
+      else
+	{
+	  if (match_len > 0)
+	    return 0;
+	  else
+	    return (elem_len > char_len) ? elem_len : char_len;
+	}
+    }
+  return 0;
+}
+
+# ifdef _LIBC
+static unsigned int
+internal_function
+find_collation_sequence_value (const unsigned char *mbs, size_t mbs_len)
+{
+  uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules == 0)
+    {
+      if (mbs_len == 1)
+	{
+	  /* No valid character.  Match it as a single byte character.  */
+	  const unsigned char *collseq = (const unsigned char *)
+	    _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+	  return collseq[mbs[0]];
+	}
+      return UINT_MAX;
+    }
+  else
+    {
+      int32_t idx;
+      const unsigned char *extra = (const unsigned char *)
+	_NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+      int32_t extrasize = (const unsigned char *)
+	_NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra;
+
+      for (idx = 0; idx < extrasize;)
+	{
+	  int mbs_cnt, found = 0;
+	  int32_t elem_mbs_len;
+	  /* Skip the name of collating element name.  */
+	  idx = idx + extra[idx] + 1;
+	  elem_mbs_len = extra[idx++];
+	  if (mbs_len == elem_mbs_len)
+	    {
+	      for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt)
+		if (extra[idx + mbs_cnt] != mbs[mbs_cnt])
+		  break;
+	      if (mbs_cnt == elem_mbs_len)
+		/* Found the entry.  */
+		found = 1;
+	    }
+	  /* Skip the byte sequence of the collating element.  */
+	  idx += elem_mbs_len;
+	  /* Adjust for the alignment.  */
+	  idx = (idx + 3) & ~3;
+	  /* Skip the collation sequence value.  */
+	  idx += sizeof (uint32_t);
+	  /* Skip the wide char sequence of the collating element.  */
+	  idx = idx + sizeof (uint32_t) * (extra[idx] + 1);
+	  /* If we found the entry, return the sequence value.  */
+	  if (found)
+	    return *(uint32_t *) (extra + idx);
+	  /* Skip the collation sequence value.  */
+	  idx += sizeof (uint32_t);
+	}
+      return UINT_MAX;
+    }
+}
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+
+/* Check whether the node accepts the byte which is IDX-th
+   byte of the INPUT.  */
+
+static int
+internal_function
+check_node_accept (const re_match_context_t *mctx, const re_token_t *node,
+		   int idx)
+{
+  unsigned char ch;
+  ch = re_string_byte_at (&mctx->input, idx);
+  switch (node->type)
+    {
+    case CHARACTER:
+      if (node->opr.c != ch)
+        return 0;
+      break;
+
+    case SIMPLE_BRACKET:
+      if (!bitset_contain (node->opr.sbcset, ch))
+        return 0;
+      break;
+
+#ifdef RE_ENABLE_I18N
+    case OP_UTF8_PERIOD:
+      if (ch >= 0x80)
+        return 0;
+      /* FALLTHROUGH */
+#endif
+    case OP_PERIOD:
+      if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE))
+	  || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL)))
+	return 0;
+      break;
+
+    default:
+      return 0;
+    }
+
+  if (node->constraint)
+    {
+      /* The node has constraints.  Check whether the current context
+	 satisfies the constraints.  */
+      unsigned int context = re_string_context_at (&mctx->input, idx,
+						   mctx->eflags);
+      if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+	return 0;
+    }
+
+  return 1;
+}
+
+/* Extend the buffers, if the buffers have run out.  */
+
+static reg_errcode_t
+internal_function
+extend_buffers (re_match_context_t *mctx)
+{
+  reg_errcode_t ret;
+  re_string_t *pstr = &mctx->input;
+
+  /* Double the lengthes of the buffers.  */
+  ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  if (mctx->state_log != NULL)
+    {
+      /* And double the length of state_log.  */
+      /* XXX We have no indication of the size of this buffer.  If this
+	 allocation fail we have no indication that the state_log array
+	 does not have the right size.  */
+      re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *,
+					      pstr->bufs_len + 1);
+      if (BE (new_array == NULL, 0))
+	return REG_ESPACE;
+      mctx->state_log = new_array;
+    }
+
+  /* Then reconstruct the buffers.  */
+  if (pstr->icase)
+    {
+#ifdef RE_ENABLE_I18N
+      if (pstr->mb_cur_max > 1)
+	{
+	  ret = build_wcs_upper_buffer (pstr);
+	  if (BE (ret != REG_NOERROR, 0))
+	    return ret;
+	}
+      else
+#endif /* RE_ENABLE_I18N  */
+	build_upper_buffer (pstr);
+    }
+  else
+    {
+#ifdef RE_ENABLE_I18N
+      if (pstr->mb_cur_max > 1)
+	build_wcs_buffer (pstr);
+      else
+#endif /* RE_ENABLE_I18N  */
+	{
+	  if (pstr->trans != NULL)
+	    re_string_translate_buffer (pstr);
+	}
+    }
+  return REG_NOERROR;
+}
+
+

+/* Functions for matching context.  */
+
+/* Initialize MCTX.  */
+
+static reg_errcode_t
+internal_function
+match_ctx_init (re_match_context_t *mctx, int eflags, int n)
+{
+  mctx->eflags = eflags;
+  mctx->match_last = -1;
+  if (n > 0)
+    {
+      mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n);
+      mctx->sub_tops = re_malloc (re_sub_match_top_t *, n);
+      if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0))
+	return REG_ESPACE;
+    }
+  /* Already zero-ed by the caller.
+     else
+       mctx->bkref_ents = NULL;
+     mctx->nbkref_ents = 0;
+     mctx->nsub_tops = 0;  */
+  mctx->abkref_ents = n;
+  mctx->max_mb_elem_len = 1;
+  mctx->asub_tops = n;
+  return REG_NOERROR;
+}
+
+/* Clean the entries which depend on the current input in MCTX.
+   This function must be invoked when the matcher changes the start index
+   of the input, or changes the input string.  */
+
+static void
+internal_function
+match_ctx_clean (re_match_context_t *mctx)
+{
+  int st_idx;
+  for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx)
+    {
+      int sl_idx;
+      re_sub_match_top_t *top = mctx->sub_tops[st_idx];
+      for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx)
+	{
+	  re_sub_match_last_t *last = top->lasts[sl_idx];
+	  re_free (last->path.array);
+	  re_free (last);
+	}
+      re_free (top->lasts);
+      if (top->path)
+	{
+	  re_free (top->path->array);
+	  re_free (top->path);
+	}
+      free (top);
+    }
+
+  mctx->nsub_tops = 0;
+  mctx->nbkref_ents = 0;
+}
+
+/* Free all the memory associated with MCTX.  */
+
+static void
+internal_function
+match_ctx_free (re_match_context_t *mctx)
+{
+  /* First, free all the memory associated with MCTX->SUB_TOPS.  */
+  match_ctx_clean (mctx);
+  re_free (mctx->sub_tops);
+  re_free (mctx->bkref_ents);
+}
+
+/* Add a new backreference entry to MCTX.
+   Note that we assume that caller never call this function with duplicate
+   entry, and call with STR_IDX which isn't smaller than any existing entry.
+*/
+
+static reg_errcode_t
+internal_function
+match_ctx_add_entry (re_match_context_t *mctx, int node, int str_idx, int from,
+		     int to)
+{
+  if (mctx->nbkref_ents >= mctx->abkref_ents)
+    {
+      struct re_backref_cache_entry* new_entry;
+      new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry,
+			      mctx->abkref_ents * 2);
+      if (BE (new_entry == NULL, 0))
+	{
+	  re_free (mctx->bkref_ents);
+	  return REG_ESPACE;
+	}
+      mctx->bkref_ents = new_entry;
+      memset (mctx->bkref_ents + mctx->nbkref_ents, '\0',
+	      sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
+      mctx->abkref_ents *= 2;
+    }
+  if (mctx->nbkref_ents > 0
+      && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx)
+    mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1;
+
+  mctx->bkref_ents[mctx->nbkref_ents].node = node;
+  mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx;
+  mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from;
+  mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to;
+
+  /* This is a cache that saves negative results of check_dst_limits_calc_pos.
+     If bit N is clear, means that this entry won't epsilon-transition to
+     an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression.  If
+     it is set, check_dst_limits_calc_pos_1 will recurse and try to find one
+     such node.
+
+     A backreference does not epsilon-transition unless it is empty, so set
+     to all zeros if FROM != TO.  */
+  mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map
+    = (from == to ? ~0 : 0);
+
+  mctx->bkref_ents[mctx->nbkref_ents++].more = 0;
+  if (mctx->max_mb_elem_len < to - from)
+    mctx->max_mb_elem_len = to - from;
+  return REG_NOERROR;
+}
+
+/* Search for the first entry which has the same str_idx, or -1 if none is
+   found.  Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX.  */
+
+static int
+internal_function
+search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx)
+{
+  int left, right, mid, last;
+  last = right = mctx->nbkref_ents;
+  for (left = 0; left < right;)
+    {
+      mid = (left + right) / 2;
+      if (mctx->bkref_ents[mid].str_idx < str_idx)
+	left = mid + 1;
+      else
+	right = mid;
+    }
+  if (left < last && mctx->bkref_ents[left].str_idx == str_idx)
+    return left;
+  else
+    return -1;
+}
+
+/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches
+   at STR_IDX.  */
+
+static reg_errcode_t
+internal_function
+match_ctx_add_subtop (re_match_context_t *mctx, int node, int str_idx)
+{
+#ifdef DEBUG
+  assert (mctx->sub_tops != NULL);
+  assert (mctx->asub_tops > 0);
+#endif
+  if (BE (mctx->nsub_tops == mctx->asub_tops, 0))
+    {
+      int new_asub_tops = mctx->asub_tops * 2;
+      re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops,
+						   re_sub_match_top_t *,
+						   new_asub_tops);
+      if (BE (new_array == NULL, 0))
+	return REG_ESPACE;
+      mctx->sub_tops = new_array;
+      mctx->asub_tops = new_asub_tops;
+    }
+  mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t));
+  if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0))
+    return REG_ESPACE;
+  mctx->sub_tops[mctx->nsub_tops]->node = node;
+  mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx;
+  return REG_NOERROR;
+}
+
+/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches
+   at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP.  */
+
+static re_sub_match_last_t *
+internal_function
+match_ctx_add_sublast (re_sub_match_top_t *subtop, int node, int str_idx)
+{
+  re_sub_match_last_t *new_entry;
+  if (BE (subtop->nlasts == subtop->alasts, 0))
+    {
+      int new_alasts = 2 * subtop->alasts + 1;
+      re_sub_match_last_t **new_array = re_realloc (subtop->lasts,
+						    re_sub_match_last_t *,
+						    new_alasts);
+      if (BE (new_array == NULL, 0))
+	return NULL;
+      subtop->lasts = new_array;
+      subtop->alasts = new_alasts;
+    }
+  new_entry = calloc (1, sizeof (re_sub_match_last_t));
+  if (BE (new_entry != NULL, 1))
+    {
+      subtop->lasts[subtop->nlasts] = new_entry;
+      new_entry->node = node;
+      new_entry->str_idx = str_idx;
+      ++subtop->nlasts;
+    }
+  return new_entry;
+}
+
+static void
+internal_function
+sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+	       re_dfastate_t **limited_sts, int last_node, int last_str_idx)
+{
+  sctx->sifted_states = sifted_sts;
+  sctx->limited_states = limited_sts;
+  sctx->last_node = last_node;
+  sctx->last_str_idx = last_str_idx;
+  re_node_set_init_empty (&sctx->limits);
+}
diff --git a/rrbb.c b/rrbb.c
new file mode 100755
index 0000000..36a780a
--- /dev/null
+++ b/rrbb.c
@@ -0,0 +1,620 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011, 2012, 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/********************************************************************************
+ *
+ * File:	rrbb.c
+ *
+ * Purpose:	Raw Received Bit Buffer.
+ *		Implementation of an array of bits used to hold data out of
+ *		the demodulator before feeding it into the HLDC decoding.
+ *
+ * Version 1.0:	Let's try something new.
+ *		Rather than storing a single bit from the demodulator
+ *		output, let's store a value which we can try later
+ *		comparing to threshold values besides 0.
+ *
+ *******************************************************************************/
+
+#define RRBB_C
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "direwolf.h"
+#include "textcolor.h"
+#include "ax25_pad.h"
+#include "rrbb.h"
+
+
+
+
+#define MAGIC1 0x12344321
+#define MAGIC2 0x56788765
+
+#ifndef SLICENDICE
+static const unsigned int masks[SOI] = {
+	0x00000001,
+	0x00000002,
+	0x00000004,
+	0x00000008,
+	0x00000010,
+	0x00000020,
+	0x00000040,
+	0x00000080,
+	0x00000100,
+	0x00000200,
+	0x00000400,
+	0x00000800,
+	0x00001000,
+	0x00002000,
+	0x00004000,
+	0x00008000,
+	0x00010000,
+	0x00020000,
+	0x00040000,
+	0x00080000,
+	0x00100000,
+	0x00200000,
+	0x00400000,
+	0x00800000,
+	0x01000000,
+	0x02000000,
+	0x04000000,
+	0x08000000,
+	0x10000000,
+	0x20000000,
+	0x40000000,
+	0x80000000 };
+#endif
+
+static int new_count = 0;
+static int delete_count = 0;
+
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_new	
+ *
+ * Purpose:	Allocate space for an array of samples.
+ *
+ * Inputs:	chan	- Radio channel from whence it came.
+ *
+ *		subchan	- Which demodulator of the channel.
+ *
+ *		is_scrambled - Is data scrambled? (true, false)
+ *
+ *		descram_state - State of data descrambler.
+ *
+ * Returns:	Handle to be used by other functions.
+ *		
+ * Description:	
+ *
+ ***********************************************************************************/
+
+rrbb_t rrbb_new (int chan, int subchan, int is_scrambled, int descram_state)
+{
+	rrbb_t result;
+
+	assert (SOI == 8 * sizeof(unsigned int));
+
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
+
+
+	result = malloc(sizeof(struct rrbb_s));
+
+	result->magic1 = MAGIC1;
+	result->chan = chan;
+	result->subchan = subchan;
+	result->magic2 = MAGIC2;
+
+	new_count++;
+
+	rrbb_clear (result, is_scrambled, descram_state);
+
+	return (result);
+}
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_clear	
+ *
+ * Purpose:	Clear by setting length to zero, etc.
+ *
+ * Inputs:	Handle for sample array.
+ *
+ ***********************************************************************************/
+
+void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	assert (is_scrambled == 0 || is_scrambled == 1);
+
+	b->nextp = NULL;
+	b->audio_level = 9999;
+	b->len = 0;
+
+	b->is_scrambled = is_scrambled;
+	b->descram_state = descram_state;
+
+}
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_append_bit	
+ *
+ * Purpose:	Append another bit to the end.
+ *
+ * Inputs:	Handle for sample array.
+ *		Value for the sample.
+ *
+ ***********************************************************************************/
+
+#if SLICENDICE
+void rrbb2_append_bit (rrbb_t b, float val)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+	assert (b->len >= 0);
+
+	if (b->len >= MAX_NUM_BITS) {
+	  return;	/* Silently discard if full. */
+	}
+
+	b->data[b->len++] = (int)(val * 1000.);
+}
+#else
+void rrbb_append_bit (rrbb_t b, int val)
+{
+	unsigned int di, mi;
+	
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	if (b->len >= MAX_NUM_BITS) {
+	  return;	/* Silently discard if full. */
+	}
+
+	di = b->len / SOI;
+	mi = b->len % SOI;
+
+	if (val) {
+	  b->data[di] |= masks[mi];
+	}
+	else {
+	  b->data[di] &= ~ masks[mi];
+	}
+
+	b->len++;
+}
+#endif
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_chop8	
+ *
+ * Purpose:	Remove 8 from the length.
+ *
+ * Inputs:	Handle for bit array.
+ *		
+ * Description:	Back up after appending the flag sequence.
+ *
+ ***********************************************************************************/
+
+void rrbb_chop8 (rrbb_t b)
+{
+	
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	if (b->len >= 8) {
+	  b->len -= 8;
+	}
+}
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_get_len	
+ *
+ * Purpose:	Get number of bits in the array.
+ *
+ * Inputs:	Handle for bit array.
+ *		
+ ***********************************************************************************/
+
+int rrbb_get_len (rrbb_t b)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	return (b->len);
+}
+
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_set_slice_val	
+ *
+ * Purpose:	Set slicing value to determine whether a sample is bit 0 or 1.
+ *
+ * Inputs:	Handle for sample array.
+ *		Slicing point value.
+ *		
+ ***********************************************************************************/
+
+#if SLICENDICE
+
+static int cmp_slice (slice_t *a, slice_t *b)
+{
+	return ( *a - *b );
+}
+
+void rrbb_set_slice_val (rrbb_t b, slice_t slice_val)
+{
+	slice_t sorted[MAX_NUM_BITS];
+	int n, i;
+	int sum, ave, median, izero;
+
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	b->slice_val = slice_val;
+
+	memcpy (sorted, b->data, b->len * sizeof(slice_t));
+
+	/* Typically takes 14 milliseconds on a reasonable PC. */
+	qsort (sorted, (size_t)(b->len), sizeof(slice_t), cmp_slice);
+
+	text_color_set (DW_COLOR_DEBUG);
+	
+	n = 0;
+	dw_printf ("[%d..%d] ", n, n+9);
+	for (i=n; i<=n+9; i++) dw_printf (" %d", sorted[i]);
+	dw_printf ("\n");
+
+	n = ( b->len / 2 ) - 10;
+	dw_printf ("m[%d..%d] ", n, n+19);
+	for (i=n; i<=n+19; i++) dw_printf (" %d", sorted[i]);
+	dw_printf ("\n");
+
+	n = b->len - 1 - 9;
+	dw_printf ("[%d..%d] ", n, n+9);
+	for (i=n; i<=n+9; i++) dw_printf (" %d", sorted[i]);
+	dw_printf ("\n");
+
+	sum = 0;
+	for (i=0; i<b->len; i++) {
+	  sum += sorted[i];
+	}
+	ave = sum / b->len;
+
+	//b->slice_val = ave;
+	//b->slice_val = sorted[b->len/2];
+	 
+	/* Find first one >= 0. */
+	izero = -1;
+	for (i=0; i<b->len; i++) {
+	  if (sorted[i] >= 0) {
+	    izero = i;
+	    break;
+	  }
+	}
+
+	if (izero >= 0) {
+	  n = izero - 10;
+	  dw_printf ("z[%d..%d] ", n, n+19);
+	  for (i=n; i<=n+19; i++) dw_printf (" %d", sorted[i]);
+	  dw_printf ("\n");
+
+	b->slice_val = sorted[izero-1];
+
+	}
+	
+	
+
+}
+
+#endif
+
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_get_bit	
+ *
+ * Purpose:	Get value of bit in specified position.
+ *
+ * Inputs:	Handle for sample array.
+ *		Index into array.
+ *		
+ ***********************************************************************************/
+
+#if SLICENDICE
+int rrbb_get_bit (rrbb_t b, unsigned int ind)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+	assert (ind >= 0 && ind < b->len);
+
+	if (b->data[ind] > b->slice_val) {
+	  return 1;
+	}
+	else {
+	  return 0;
+	}
+}
+#else
+int rrbb_get_bit (rrbb_t b, unsigned int ind)
+{
+	unsigned int di, mi;
+
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	assert (ind < b->len);
+
+	di = ind / SOI;
+	mi = ind % SOI;
+
+	if (b->data[di] & masks[mi]) {
+	  return 1;
+	}
+	else {
+	  return 0;
+	}
+}
+#endif
+
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_flip_bit	
+ *
+ * Purpose:	Complement the value of bit in specified position.
+ *
+ * Inputs:	Handle for bit array.
+ *		Index into array.
+ *		
+ ***********************************************************************************/
+
+//void rrbb_flip_bit (rrbb_t b, unsigned int ind)
+//{
+//	unsigned int di, mi;
+//
+//	assert (b != NULL);
+//	assert (b->magic1 == MAGIC1);
+//	assert (b->magic2 == MAGIC2);
+//
+//	assert (ind < b->len);
+//
+//	di = ind / SOI;
+//	mi = ind % SOI;
+//
+//	b->data[di] ^= masks[mi];
+//}
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_delete	
+ *
+ * Purpose:	Free the storage associated with the bit array.
+ *
+ * Inputs:	Handle for bit array.
+ *		
+ ***********************************************************************************/
+
+void rrbb_delete (rrbb_t b)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	b->magic1 = 0;
+	b->magic2 = 0;
+	
+	free (b);
+
+	delete_count++;
+}
+
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_set_netxp	
+ *
+ * Purpose:	Set the nextp field, used to maintain a queue.
+ *
+ * Inputs:	b	Handle for bit array.
+ *		np	New value for nextp.
+ *		
+ ***********************************************************************************/
+
+void rrbb_set_nextp (rrbb_t b, rrbb_t np)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	b->nextp = np;
+}
+
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_get_netxp	
+ *
+ * Purpose:	Get value of nextp field.
+ *
+ * Inputs:	b	Handle for bit array.
+ *		
+ ***********************************************************************************/
+
+rrbb_t rrbb_get_nextp (rrbb_t b)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	return (b->nextp);
+}
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_get_chan	
+ *
+ * Purpose:	Get channel from which bit buffer was received.
+ *
+ * Inputs:	b	Handle for bit array.
+ *		
+ ***********************************************************************************/
+
+int rrbb_get_chan (rrbb_t b)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	assert (b->chan >= 0 && b->chan < MAX_CHANS);
+
+	return (b->chan);
+}
+
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_get_subchan	
+ *
+ * Purpose:	Get subchannel from which bit buffer was received.
+ *
+ * Inputs:	b	Handle for bit array.
+ *		
+ ***********************************************************************************/
+
+int rrbb_get_subchan (rrbb_t b)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	assert (b->subchan >= 0 && b->subchan < MAX_SUBCHANS);
+
+	return (b->subchan);
+}
+
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_set_audio_level	
+ *
+ * Purpose:	Set audio level at time the frame was received.
+ *
+ * Inputs:	b	Handle for bit array.
+ *		a	Audio level.
+ *		
+ ***********************************************************************************/
+
+void rrbb_set_audio_level (rrbb_t b, int a)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	b->audio_level = a;
+}
+
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_get_audio_level	
+ *
+ * Purpose:	Get audio level at time the frame was received.
+ *
+ * Inputs:	b	Handle for bit array.
+ *		
+ ***********************************************************************************/
+
+int rrbb_get_audio_level (rrbb_t b)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	return (b->audio_level);
+}
+
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_get_is_scrambled	
+ *
+ * Purpose:	Find out if using scrambled data.
+ *
+ * Inputs:	b	Handle for bit array.
+ *
+ * Returns:	True (for 9600 baud) or false (for slower AFSK).
+ *		
+ ***********************************************************************************/
+
+int rrbb_get_is_scrambled (rrbb_t b)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	return (b->is_scrambled);
+}
+
+
+
+/***********************************************************************************
+ *
+ * Name:	rrbb_get_descram_state	
+ *
+ * Purpose:	Get data descrambler state before first data bit of frame.
+ *
+ * Inputs:	b	Handle for bit array.
+ *		
+ ***********************************************************************************/
+
+int rrbb_get_descram_state (rrbb_t b)
+{
+	assert (b != NULL);
+	assert (b->magic1 == MAGIC1);
+	assert (b->magic2 == MAGIC2);
+
+	return (b->descram_state);
+}
+
+
+/* end rrbb.c */
+
+
diff --git a/rrbb.h b/rrbb.h
new file mode 100755
index 0000000..ae78bb1
--- /dev/null
+++ b/rrbb.h
@@ -0,0 +1,102 @@
+
+#ifndef RRBB_H
+
+#define RRBB_H
+
+
+/* Try something new in version 1.0 */
+/* Get back to this later.  Disable for now. */
+
+//#define SLICENDICE 1
+
+typedef short slice_t;
+
+
+#ifdef RRBB_C
+
+/* 
+ * Maximum size (in bytes) of an AX.25 frame including the 2 octet FCS. 
+ */
+
+#define MAX_FRAME_LEN ((AX25_MAX_PACKET_LEN) + 2)	
+
+/*
+ * Maximum number of bits in AX.25 frame excluding the flags.
+ * Adequate for extreme case of bit stuffing after every 5 bits
+ * which could never happen.
+ */
+
+#define MAX_NUM_BITS (MAX_FRAME_LEN * 8 * 6 / 5)
+
+#define SOI 32
+
+typedef struct rrbb_s {
+	int magic1;
+	struct rrbb_s* nextp;	/* Next pointer to maintain a queue. */
+	int chan;		/* Radio channel from which it was received. */
+	int subchan;		/* Which modem when more than one per channel. */
+	int audio_level;	/* Received audio level at time of frame capture. */
+	unsigned int len;	/* Current number of samples in array. */
+
+	int is_scrambled;	/* Is data scrambled G3RUH / K9NG style? */
+	int descram_state;	/* Descrambler state before first data bit of frame. */
+
+#if SLICENDICE
+	slice_t slice_val;
+	slice_t data[MAX_NUM_BITS];
+#else
+	unsigned int data[(MAX_NUM_BITS+SOI-1)/SOI];
+#endif
+	int magic2;
+} *rrbb_t;
+
+#else
+
+/* Hide the implementation. */
+
+typedef void *rrbb_t;
+
+#endif
+
+
+
+rrbb_t rrbb_new (int chan, int subchan, int is_scrambled, int descram_state);
+
+void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state);
+
+#if SLICENDICE
+void rrbb2_append_bit (rrbb_t b, float val);
+#else
+void rrbb_append_bit (rrbb_t b, int val);
+#endif
+
+void rrbb_chop8 (rrbb_t b);
+
+int rrbb_get_len (rrbb_t b);
+
+#if SLICENDICE
+void rrbb_set_slice_val (rrbb_t b, slice_t slice_val);
+#endif
+
+int rrbb_get_bit (rrbb_t b, unsigned int ind);
+
+//void rrbb_flip_bit (rrbb_t b, unsigned int ind);
+
+void rrbb_delete (rrbb_t b);
+
+void rrbb_set_nextp (rrbb_t b, rrbb_t np);
+
+rrbb_t rrbb_get_nextp (rrbb_t b);
+
+int rrbb_get_chan (rrbb_t b);
+int rrbb_get_subchan (rrbb_t b);
+
+void rrbb_set_audio_level (rrbb_t b, int a);
+
+int rrbb_get_audio_level (rrbb_t b);
+
+int rrbb_get_is_scrambled (rrbb_t b);
+
+int rrbb_get_descram_state (rrbb_t b);
+
+#endif
\ No newline at end of file
diff --git a/server.c b/server.c
new file mode 100755
index 0000000..5462197
--- /dev/null
+++ b/server.c
@@ -0,0 +1,1249 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      server.c
+ *
+ * Purpose:   	Provide service to other applications via "AGW TCPIP Socket Interface".
+ *		
+ * Input:	
+ *
+ * Outputs:	  
+ *
+ * Description:	This provides a TCP socket for communication with a client application.
+ *		It implements a subset of the AGW socket interface.
+ *
+ *		Commands from application recognized:
+ *
+ *			'R'	Request for version number.
+ *				(See below for response.)
+ *
+ *			'G'	Ask about radio ports.
+ *				(See below for response.)
+ *
+ *			'g'	Capabilities of a port.  (new in 0.8)
+ *				(See below for response.)
+ *
+ *			'k'	Ask to start receiving RAW AX25 frames.
+ *
+ *			'm'	Ask to start receiving Monitor AX25 frames.
+ *
+ *			'V'	Transmit UI data frame.
+ *				Generate audio for transmission.
+ *
+ *			'H'	Report recently heard stations.  Not implemented yet.
+ *
+ *			'K'	Transmit raw AX.25 frame.
+ *		
+ *			'X'	Register CallSign 
+ *		
+ *			'x'	Unregister CallSign 
+ *		
+ *			A message is printed if any others are received.
+ *
+ *			TODO: Should others be implemented?
+ *				
+ *
+ *		Messages sent to client application:
+ *
+ *			'R'	Reply to Request for version number.
+ *				Currently responds with major 1, minor 0.
+ *
+ *			'G'	Reply to Ask about radio ports.
+ *
+ *			'g'	Reply to capabilities of a port.  (new in 0.8)
+ *
+ *			'K'	Received AX.25 frame in raw format.
+ *				(Enabled with 'k' command.)
+ *
+ *			'U'	Received AX.25 frame in monitor format.
+ *				(Enabled with 'm' command.)
+ *
+ *
+ *
+ * References:	AGWPE TCP/IP API Tutorial
+ *		http://uz7ho.org.ua/includes/agwpeapi.htm
+ *
+ * 		Getting Started with Winsock
+ *		http://msdn.microsoft.com/en-us/library/windows/desktop/bb530742(v=vs.85).aspx
+ *
+ *---------------------------------------------------------------*/
+
+
+/*
+ * Native Windows:	Use the Winsock interface.
+ * Linux:		Use the BSD socket interface.
+ * Cygwin:		Can use either one.
+ */
+
+
+#if __WIN32__
+#include <winsock2.h>
+#define _WIN32_WINNT 0x0501
+#include <ws2tcpip.h>
+#else 
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "direwolf.h"
+#include "tq.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "audio.h"
+#include "server.h"
+
+
+
+static int client_sock;		/* File descriptor for socket for */
+				/* communication with client application. */
+				/* Set to -1 if not connected. */
+				/* (Don't use SOCKET type because it is unsigned.) */
+
+static int enable_send_raw_to_client;	/* Should we send received packets to client app? */
+static int enable_send_monitor_to_client;
+
+
+static int num_channels;	/* Number of radio ports. */
+
+
+static void * connect_listen_thread (void *arg);
+static void * cmd_listen_thread (void *arg);
+
+/*
+ * Message header for AGW protocol.
+ * Assuming little endian such as x86 or ARM.
+ * Byte swapping would be required for big endian cpu.
+ */
+
+#if __GNUC__
+#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
+#error This needs to be more portable to work on big endian.
+#endif
+#endif
+
+struct agwpe_s {	
+  short portx;			/* 0 for first, 1 for second, etc. */
+  short port_hi_reserved;	
+  short kind_lo;		/* message type */
+  short kind_hi;
+  char call_from[10];
+  char call_to[10];
+  int data_len;			/* Number of data bytes following. */
+  int user_reserved;
+};
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        debug_print 
+ *
+ * Purpose:     Print message to/from client for debugging.
+ *
+ * Inputs:	fromto		- Direction of message.
+ *		pmsg		- Address of the message block.
+ *		msg_len		- Length of the message.
+ *
+ *--------------------------------------------------------------------*/
+
+static int debug_client = 0;		/* Print information flowing from and to client. */
+
+void server_set_debug (int n) 
+{	
+	debug_client = n;
+}
+
+void hex_dump (unsigned char *p, int len) 
+{
+	int n, i, offset;
+
+	offset = 0;
+	while (len > 0) {
+	  n = len < 16 ? len : 16; 
+	  dw_printf ("  %03x: ", offset);
+	  for (i=0; i<n; i++) {
+	    dw_printf (" %02x", p[i]);
+	  }
+	  for (i=n; i<16; i++) {
+	    dw_printf ("   ");
+	  }
+	  dw_printf ("  ");
+	  for (i=0; i<n; i++) {
+	    dw_printf ("%c", isprint(p[i]) ? p[i] : '.');
+	  }
+	  dw_printf ("\n");
+	  p += 16;
+	  offset += 16;
+	  len -= 16;
+	}
+}
+
+typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t;
+
+static void debug_print (fromto_t fromto, struct agwpe_s *pmsg, int msg_len)
+{
+	char direction [10];
+	char datakind[80];
+	const char *prefix [2] = { "<<<", ">>>" };
+
+	switch (fromto) {
+
+	  case FROM_CLIENT:
+	    strcpy (direction, "from");		/* from the client application */
+
+	    switch (pmsg->kind_lo) {
+	      case 'P': strcpy (datakind, "Application Login"); break;
+	      case 'X': strcpy (datakind, "Register CallSign"); break;
+	      case 'x': strcpy (datakind, "Unregister CallSign"); break;
+	      case 'G': strcpy (datakind, "Ask Port Information"); break;
+	      case 'm': strcpy (datakind, "Enable Reception of Monitoring Frames"); break;
+	      case 'R': strcpy (datakind, "AGWPE Version Info"); break;
+	      case 'g': strcpy (datakind, "Ask Port Capabilities"); break;
+	      case 'H': strcpy (datakind, "Callsign Heard on a Port"); break;
+	      case 'y': strcpy (datakind, "Ask Outstanding frames waiting on a Port"); break;
+	      case 'Y': strcpy (datakind, "Ask Outstanding frames waiting for a connection"); break;
+	      case 'M': strcpy (datakind, "Send UNPROTO Information"); break;
+	      case 'C': strcpy (datakind, "Connect, Start an AX.25 Connection"); break;
+	      case 'D': strcpy (datakind, "Send Connected Data"); break;
+	      case 'd': strcpy (datakind, "Disconnect, Terminate an AX.25 Connection"); break;
+	      case 'v': strcpy (datakind, "Connect VIA, Start an AX.25 circuit thru digipeaters"); break;
+	      case 'V': strcpy (datakind, "Send UNPROTO VIA"); break;
+	      case 'c': strcpy (datakind, "Non-Standard Connections, Connection with PID"); break;
+	      case 'K': strcpy (datakind, "Send data in raw AX.25 format"); break;
+	      case 'k': strcpy (datakind, "Activate reception of Frames in raw format"); break;
+	      default:  strcpy (datakind, "**INVALID**"); break;
+	    }
+	    break;
+
+	  case TO_CLIENT:
+	  default:
+	    strcpy (direction, "to");	/* sent to the client application. */
+
+	    switch (pmsg->kind_lo) {
+	      case 'R': strcpy (datakind, "Version Number"); break;
+	      case 'X': strcpy (datakind, "Callsign Registration"); break;
+	      case 'G': strcpy (datakind, "Port Information"); break;
+	      case 'g': strcpy (datakind, "Capabilities of a Port"); break;
+	      case 'y': strcpy (datakind, "Frames Outstanding on a Port"); break;
+	      case 'Y': strcpy (datakind, "Frames Outstanding on a Connection"); break;
+	      case 'H': strcpy (datakind, "Heard Stations on a Port"); break;
+	      case 'C': strcpy (datakind, "AX.25 Connection Received"); break;
+	      case 'D': strcpy (datakind, "Connected AX.25 Data"); break;
+	      case 'M': strcpy (datakind, "Monitored Connected Information"); break;
+	      case 'S': strcpy (datakind, "Monitored Supervisory Information"); break;
+	      case 'U': strcpy (datakind, "Monitored Unproto Information"); break;
+	      case 'T': strcpy (datakind, "Monitoring Own Information"); break;
+	      case 'K': strcpy (datakind, "Monitored Information in Raw Format"); break;
+	      default:  strcpy (datakind, "**INVALID**"); break;
+	    }
+	}
+
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("\n");
+
+	dw_printf ("%s %s %s AGWPE client application, total length = %d\n",
+			prefix[(int)fromto], datakind, direction, msg_len);
+
+	dw_printf ("\tportx = %d, port_hi_reserved = %d\n", pmsg->portx, pmsg->port_hi_reserved);
+	dw_printf ("\tkind_lo = %d = '%c', kind_hi = %d\n", pmsg->kind_lo, pmsg->kind_lo, pmsg->kind_hi);
+	dw_printf ("\tcall_from = \"%s\", call_to = \"%s\"\n", pmsg->call_from, pmsg->call_to);
+	dw_printf ("\tdata_len = %d, user_reserved = %d, data =\n", pmsg->data_len, pmsg->user_reserved);
+
+	hex_dump ((char*)pmsg + sizeof(struct agwpe_s), pmsg->data_len);
+
+	if (msg_len < 36) {
+	  text_color_set (DW_COLOR_ERROR);
+	  dw_printf ("AGWPE message length, %d, is shorter than minumum 36.\n", msg_len);
+	}
+	if (msg_len != pmsg->data_len + 36) {
+	  text_color_set (DW_COLOR_ERROR);
+	  dw_printf ("AGWPE message length, %d, inconsistent with data length %d.\n", msg_len, pmsg->data_len);
+	}
+
+}
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        server_init
+ *
+ * Purpose:     Set up a server to listen for connection requests from
+ *		an application such as Xastir.
+ *
+ * Inputs:	mc->agwpe_port	- TCP port for server.
+ *				  Main program has default of 8000 but allows
+ *				  an alternative to be specified on the command line
+ *
+ * Outputs:	
+ *
+ * Description:	This starts two threads:
+ *		  *  to listen for a connection from client app.
+ *		  *  to listen for commands from client app.
+ *		so the main application doesn't block while we wait for these.
+ *
+ *--------------------------------------------------------------------*/
+
+
+void server_init (struct misc_config_s *mc)
+{
+#if __WIN32__
+	HANDLE connect_listen_th;
+	HANDLE cmd_listen_th;
+#else
+	pthread_t connect_listen_tid;
+	pthread_t cmd_listen_tid;
+#endif
+	int e;
+	int server_port = mc->agwpe_port;
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("server_init ( %d )\n", server_port);
+	debug_a = 1;
+#endif
+	client_sock = -1;
+	enable_send_raw_to_client = 0;
+	enable_send_monitor_to_client = 0;
+	num_channels = mc->num_channels;
+
+/*
+ * This waits for a client to connect and sets client_sock.
+ */
+#if __WIN32__
+	connect_listen_th = _beginthreadex (NULL, 0, connect_listen_thread, (void *)server_port, 0, NULL);
+	if (connect_listen_th == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not create AGW connect listening thread\n");
+	  return;
+	}
+#else
+	e = pthread_create (&connect_listen_tid, NULL, connect_listen_thread, (void *)(long)server_port);
+	if (e != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror("Could not create AGW connect listening thread");
+	  return;
+	}
+#endif
+
+/*
+ * This reads messages from client when client_sock is valid.
+ */
+#if __WIN32__
+	cmd_listen_th = _beginthreadex (NULL, 0, cmd_listen_thread, NULL, 0, NULL);
+	if (cmd_listen_th == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not create AGW command listening thread\n");
+	  return;
+	}
+#else
+	e = pthread_create (&cmd_listen_tid, NULL, cmd_listen_thread, NULL);
+	if (e != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror("Could not create AGW command listening thread");
+	  return;
+	}
+#endif
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        connect_listen_thread
+ *
+ * Purpose:     Wait for a connection request from an application.
+ *
+ * Inputs:	arg		- TCP port for server.
+ *				  Main program has default of 8000 but allows
+ *				  an alternative to be specified on the command line
+ *
+ * Outputs:	client_sock	- File descriptor for communicating with client app.
+ *
+ * Description:	Wait for connection request from client and establish
+ *		communication.
+ *		Note that the client can go away and come back again and
+ *		re-establish communication without restarting this application.
+ *
+ *--------------------------------------------------------------------*/
+
+static void * connect_listen_thread (void *arg)
+{
+#if __WIN32__
+
+	struct addrinfo hints;
+	struct addrinfo *ai = NULL;
+	int err;
+	char server_port_str[12];
+
+	SOCKET listen_sock;  
+	WSADATA wsadata;
+
+	sprintf (server_port_str, "%d", (int)(long)arg);
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+        dw_printf ("DEBUG: serverport = %d = '%s'\n", (int)(long)arg, server_port_str);
+#endif
+	err = WSAStartup (MAKEWORD(2,2), &wsadata);
+	if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("WSAStartup failed: %d\n", err);
+	    return (NULL);
+	}
+
+	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
+	  text_color_set(DW_COLOR_ERROR);
+          dw_printf("Could not find a usable version of Winsock.dll\n");
+          WSACleanup();
+	  //sleep (1);
+          return (NULL);
+	}
+
+	memset (&hints, 0, sizeof(hints));
+	hints.ai_family = AF_INET;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_protocol = IPPROTO_TCP;
+	hints.ai_flags = AI_PASSIVE;
+
+	err = getaddrinfo(NULL, server_port_str, &hints, &ai);
+	if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf("getaddrinfo failed: %d\n", err);
+	    //sleep (1);
+	    WSACleanup();
+	    return (NULL);
+	}
+
+	listen_sock= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+	if (listen_sock == INVALID_SOCKET) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("connect_listen_thread: Socket creation failed, err=%d", WSAGetLastError());
+	  return (NULL);
+	}
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+    	dw_printf("Binding to port %s ... \n", server_port_str);
+#endif
+
+	err = bind( listen_sock, ai->ai_addr, (int)ai->ai_addrlen);
+	if (err == SOCKET_ERROR) {
+	  text_color_set(DW_COLOR_ERROR);
+          dw_printf("Bind failed with error: %d\n", WSAGetLastError());
+	  dw_printf("Some other application is probably already using port %s.\n", server_port_str);
+          freeaddrinfo(ai);
+          closesocket(listen_sock);
+          WSACleanup();
+          return (NULL);
+        }
+
+	freeaddrinfo(ai);
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+ 	dw_printf("opened socket as fd (%d) on port (%s) for stream i/o\n", listen_sock, server_port_str );
+#endif
+
+ 	while (1) {
+  	 
+	  while (client_sock > 0) {
+	    SLEEP_SEC(1);			/* Already connected.  Try again later. */
+	  }
+
+#define QUEUE_SIZE 5
+
+	  if(listen(listen_sock,QUEUE_SIZE) == SOCKET_ERROR)
+	  {
+	    text_color_set(DW_COLOR_ERROR);
+            dw_printf("Listen failed with error: %d\n", WSAGetLastError());
+	    return (NULL);
+	  }
+	
+	  text_color_set(DW_COLOR_INFO);
+          dw_printf("Ready to accept AGW client application on port %s ...\n", server_port_str);
+         
+          client_sock = accept(listen_sock, NULL, NULL);
+
+	  if (client_sock == -1) {
+	    text_color_set(DW_COLOR_ERROR);
+            dw_printf("Accept failed with error: %d\n", WSAGetLastError());
+            closesocket(listen_sock);
+            WSACleanup();
+            return (NULL);
+          }
+
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf("\nConnected to AGW client application ...\n\n");
+
+/*
+ * The command to change this is actually a toggle, not explicit on or off.
+ * Make sure it has proper state when we get a new connection.
+ */ 
+	  enable_send_raw_to_client = 0;
+	  enable_send_monitor_to_client = 0;
+
+ 	}
+
+#else
+
+    	struct sockaddr_in sockaddr; /* Internet socket address stuct */
+    	socklen_t sockaddr_size = sizeof(struct sockaddr_in);
+	int server_port = (int)(long)arg;
+	int listen_sock;  
+
+	listen_sock= socket(AF_INET,SOCK_STREAM,0);
+	if (listen_sock == -1) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror ("connect_listen_thread: Socket creation failed");
+	  return (NULL);
+	}
+
+    	sockaddr.sin_addr.s_addr = INADDR_ANY;
+    	sockaddr.sin_port = htons(server_port);
+    	sockaddr.sin_family = AF_INET;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+    	dw_printf("Binding to port %d ... \n", server_port);
+#endif
+
+        if (bind(listen_sock,(struct sockaddr*)&sockaddr,sizeof(sockaddr))  == -1) {
+	  text_color_set(DW_COLOR_ERROR);
+    	  perror ("connect_listen_thread: Bind failed");
+          return (NULL);
+	}
+
+	getsockname( listen_sock, (struct sockaddr *)(&sockaddr), &sockaddr_size);
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+ 	dw_printf("opened socket as fd (%d) on port (%d) for stream i/o\n", listen_sock, ntohs(sockaddr.sin_port) );
+#endif
+
+ 	while (1) {
+  	 
+	  while (client_sock > 0) {
+	    SLEEP_SEC(1);			/* Already connected.  Try again later. */
+	  }
+
+#define QUEUE_SIZE 5
+
+	  if(listen(listen_sock,QUEUE_SIZE) == -1)
+	  {
+	    text_color_set(DW_COLOR_ERROR);
+	    perror ("connect_listen_thread: Listen failed");
+	    return (NULL);
+	  }
+	
+	  text_color_set(DW_COLOR_INFO);
+          dw_printf("Ready to accept AGW client application on port %d ...\n", server_port);
+         
+          client_sock = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size);
+
+	  text_color_set(DW_COLOR_INFO);
+	  dw_printf("\nConnected to AGW client application ...\n\n");
+
+/*
+ * The command to change this is actually a toggle, not explicit on or off.
+ * Make sure it has proper state when we get a new connection.
+ */ 
+	  enable_send_raw_to_client = 0;
+	  enable_send_monitor_to_client = 0;
+
+ 	}
+#endif
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        server_send_rec_packet
+ *
+ * Purpose:     Send a received packet to the client app.
+ *
+ * Inputs:	chan		- Channel number where packet was received.
+ *				  0 = first, 1 = second if any.
+ *
+ *		pp		- Identifier for packet object.
+ *
+ *		fbuf		- Address of raw received frame buffer.
+ *		flen		- Length of raw received frame.
+ *		
+ *
+ * Description:	Send message to client if connected.
+ *		Disconnect from client, and notify user, if any error.
+ *
+ *		There are two different formats:
+ *			RAW - the original received frame.
+ *			MONITOR - just the information part.
+ *
+ *--------------------------------------------------------------------*/
+
+
+void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf,  int flen)
+{
+	struct {	
+	  struct agwpe_s hdr;
+	  char data[1+AX25_MAX_PACKET_LEN];		
+	} agwpe_msg;
+
+	int err;
+	int info_len;
+	unsigned char *pinfo;
+
+/*
+ * RAW format
+ */
+	
+	if (enable_send_raw_to_client 
+			&& client_sock > 0){
+
+	    memset (&agwpe_msg.hdr, 0, sizeof(agwpe_msg.hdr));
+
+	    agwpe_msg.hdr.portx = chan;
+
+	    agwpe_msg.hdr.kind_lo = 'K';
+
+	    ax25_get_addr_with_ssid (pp, AX25_SOURCE, agwpe_msg.hdr.call_from);
+
+	    ax25_get_addr_with_ssid (pp, AX25_DESTINATION, agwpe_msg.hdr.call_to);
+
+	    agwpe_msg.hdr.data_len = flen + 1;
+
+	    /* Stick in extra byte for the "TNC" to use. */
+
+	    agwpe_msg.data[0] = 0;
+	    memcpy (agwpe_msg.data + 1, fbuf, (size_t)flen);
+
+	    if (debug_client) {
+	      debug_print (TO_CLIENT, &agwpe_msg.hdr, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len);
+	    }
+
+#if __WIN32__	
+            err = send (client_sock, (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len, 0);
+	    if (err == SOCKET_ERROR)
+	    {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("\nError %d sending message to AGW client application.  Closing connection.\n\n", WSAGetLastError());
+	      closesocket (client_sock);
+	      client_sock = -1;
+	      WSACleanup();
+	    }
+#else
+            err = write (client_sock, &agwpe_msg, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len);
+	    if (err <= 0)
+	    {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("\nError sending message to AGW client application.  Closing connection.\n\n");
+	      close (client_sock);
+	      client_sock = -1;    
+	    }
+#endif
+	}
+
+
+/* MONITOR format - only for UI frames. */
+
+	
+	if (enable_send_monitor_to_client 
+			&& client_sock > 0 
+			&& ax25_get_control(pp) == AX25_UI_FRAME){
+
+	    time_t clock;
+	    struct tm *tm;
+
+	    clock = time(NULL);
+	    tm = localtime(&clock);
+
+	    memset (&agwpe_msg.hdr, 0, sizeof(agwpe_msg.hdr));
+
+	    agwpe_msg.hdr.portx = chan;
+
+	    agwpe_msg.hdr.kind_lo = 'U';
+
+	    ax25_get_addr_with_ssid (pp, AX25_SOURCE, agwpe_msg.hdr.call_from);
+
+	    ax25_get_addr_with_ssid (pp, AX25_DESTINATION, agwpe_msg.hdr.call_to);
+
+	    info_len = ax25_get_info (pp, &pinfo);
+
+	    /* http://uz7ho.org.ua/includes/agwpeapi.htm#_Toc500723812 */
+
+	    /* Description mentions one CR character after timestamp but example has two. */
+	    /* Actual observed cases have only one. */
+	    /* Also need to add extra CR, CR, null at end. */
+	    /* The documentation example includes these 3 extra in the Len= value */
+	    /* but actual observed data uses only the packet info length. */
+
+	    sprintf (agwpe_msg.data, " %d:Fm %s To %s <UI pid=%02X Len=%d >[%02d:%02d:%02d]\r%s\r\r",
+			chan+1, agwpe_msg.hdr.call_from, agwpe_msg.hdr.call_to,
+			ax25_get_pid(pp), info_len, 
+			tm->tm_hour, tm->tm_min, tm->tm_sec,
+			pinfo);
+
+	    agwpe_msg.hdr.data_len = strlen(agwpe_msg.data) + 1 /* include null */ ;
+
+	    if (debug_client) {
+	      debug_print (TO_CLIENT, &agwpe_msg.hdr, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len);
+	    }
+
+#if __WIN32__	
+            err = send (client_sock, (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len, 0);
+	    if (err == SOCKET_ERROR)
+	    {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("\nError %d sending message to AGW client application.  Closing connection.\n\n", WSAGetLastError());
+	      closesocket (client_sock);
+	      client_sock = -1;
+	      WSACleanup();
+	    }
+#else
+            err = write (client_sock, &agwpe_msg, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len);
+	    if (err <= 0)
+	    {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("\nError sending message to AGW client application.  Closing connection.\n\n");
+	      close (client_sock);
+	      client_sock = -1;    
+	    }
+#endif
+	}
+
+} /* server_send_rec_packet */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        read_from_socket
+ *
+ * Purpose:     Read from socket until we have desired number of bytes.
+ *
+ * Inputs:	fd		- file descriptor.
+ *		ptr		- address where data should be placed.
+ *		len		- desired number of bytes.
+ *
+ * Description:	Just a wrapper for the "read" system call but it should
+ *		never return fewer than the desired number of bytes.
+ *
+ *--------------------------------------------------------------------*/
+
+static int read_from_socket (int fd, char *ptr, int len)
+{
+	int got_bytes = 0;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("read_from_socket (%d, %p, %d)\n", fd, ptr, len);
+#endif
+	while (got_bytes < len) {
+	  int n;
+
+#if __WIN32__
+
+//TODO: any flags for send/recv?
+
+	  n = recv (fd, ptr + got_bytes, len - got_bytes, 0);
+#else
+	  n = read (fd, ptr + got_bytes, len - got_bytes);
+#endif
+
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("read_from_socket: n = %d\n", n);
+#endif
+	  if (n <= 0) {
+	    return (n);
+	  }
+
+	  got_bytes += n;
+	}
+	assert (got_bytes >= 0 && got_bytes <= len);
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("read_from_socket: return %d\n", got_bytes);
+#endif
+	return (got_bytes);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        cmd_listen_thread
+ *
+ * Purpose:     Wait for command messages from an application.
+ *
+ * Inputs:	arg		- Not used.
+ *
+ * Outputs:	client_sock	- File descriptor for communicating with client app.
+ *
+ * Description:	Process messages from the client application.
+ *		Note that the client can go away and come back again and
+ *		re-establish communication without restarting this application.
+ *
+ *--------------------------------------------------------------------*/
+
+static void * cmd_listen_thread (void *arg)
+{
+	int n;
+
+
+	struct {
+	  struct agwpe_s hdr;		/* Command header. */
+	
+	  char data[512];		/* Additional data used by some commands. */
+					/* Maximum for 'V': 1 + 8*10 + 256 */
+	} cmd;
+
+	while (1) {
+
+	  while (client_sock <= 0) {
+	    SLEEP_SEC(1);			/* Not connected.  Try again later. */
+	  }
+
+	  n = read_from_socket (client_sock, (char *)(&cmd.hdr), sizeof(cmd.hdr));
+	  if (n != sizeof(cmd.hdr)) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("\nError getting message header from client application.\n");
+	    dw_printf ("Tried to read %d bytes but got only %d.\n", (int)sizeof(cmd.hdr), n);
+	    dw_printf ("Closing connection.\n\n");
+#if __WIN32__
+	    closesocket (client_sock);
+#else
+	    close (client_sock);
+#endif
+	    client_sock = -1;
+	    continue;
+	  }
+
+	  assert (cmd.hdr.data_len >= 0 && cmd.hdr.data_len < sizeof(cmd.data));
+
+	  cmd.data[0] = '\0';
+
+	  if (cmd.hdr.data_len > 0) {
+	    n = read_from_socket (client_sock, cmd.data, cmd.hdr.data_len);
+	    if (n != cmd.hdr.data_len) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("\nError getting message data from client application.\n");
+	      dw_printf ("Tried to read %d bytes but got only %d.\n", cmd.hdr.data_len, n);
+	      dw_printf ("Closing connection.\n\n");
+#if __WIN32__
+	      closesocket (client_sock);
+#else
+	      close (client_sock);
+#endif
+	      client_sock = -1;
+	      return NULL;
+	    }
+	    if (n > 0) {
+		cmd.data[cmd.hdr.data_len] = '\0';
+	    }
+	  }
+
+/*
+ * print & process message from client.
+ */
+
+	  if (debug_client) {
+	    debug_print (FROM_CLIENT, &cmd.hdr, sizeof(cmd.hdr) + cmd.hdr.data_len);
+	  }
+
+	  switch (cmd.hdr.kind_lo) {
+
+	    case 'R':				/* Request for version number */
+	      {
+		struct {
+		  struct agwpe_s hdr;
+	 	  int major_version;
+	 	  int minor_version;
+		} reply;
+
+
+	        memset (&reply, 0, sizeof(reply));
+	        reply.hdr.kind_lo = 'R';
+	        reply.hdr.data_len = sizeof(reply.major_version) + sizeof(reply.minor_version);
+		assert (reply.hdr.data_len ==8);
+
+		// Xastir only prints this and doesn't care otherwise.
+		// APRSIS32 doesn't seem to care.
+		// UI-View32 wants on 2000.15 or later.
+
+	        reply.major_version = 2005;
+	        reply.minor_version = 127;
+
+		assert (sizeof(reply) == 44);
+
+	        if (debug_client) {
+	          debug_print (TO_CLIENT, &reply.hdr, sizeof(reply));
+	        }
+
+// TODO:  Should have unified function instead of multiple versions everywhere.
+
+#if __WIN32__	      
+	        send (client_sock, (char*)(&reply), sizeof(reply), 0);
+#else
+	        write (client_sock, &reply, sizeof(reply));
+#endif
+	      }
+	      break;
+
+	    case 'G':				/* Ask about radio ports */
+
+	      {
+		struct {
+		  struct agwpe_s hdr;
+	 	  char info[100];
+		} reply;
+
+
+	        memset (&reply, 0, sizeof(reply));
+	        reply.hdr.kind_lo = 'G';
+	        reply.hdr.data_len = sizeof (reply.info);
+
+		// Xastir only prints this and doesn't care otherwise.
+		// YAAC uses this to identify available channels.
+
+		if (num_channels == 1) {
+		  sprintf (reply.info, "1;Port1 Single channel;");
+		}
+		else {
+		  sprintf (reply.info, "2;Port1 Left channel;Port2 Right Channel;");
+		}
+
+		assert (reply.hdr.data_len == 100);
+
+	        if (debug_client) {
+	          debug_print (TO_CLIENT, &reply.hdr, sizeof(reply));
+	        }
+
+#if __WIN32__     
+	        send (client_sock, (char*)(&reply), sizeof(reply), 0);
+#else
+	        write (client_sock, &reply, sizeof(reply));
+#endif
+	      }
+	      break;
+
+
+	    case 'g':				/* Ask about capabilities of a port. */
+
+	      {
+		struct {
+		  struct agwpe_s hdr;
+	 	  unsigned char on_air_baud_rate; 	/* 0=1200, 3=9600 */
+		  unsigned char traffic_level;		/* 0xff if not in autoupdate mode */
+		  unsigned char tx_delay;
+		  unsigned char tx_tail;
+		  unsigned char persist;
+		  unsigned char slottime;
+		  unsigned char maxframe;
+		  unsigned char active_connections;
+		  int how_many_bytes;
+		} reply;
+
+
+	        memset (&reply, 0, sizeof(reply));
+
+		reply.hdr.portx = cmd.hdr.portx;	/* Reply with same port number ! */
+	        reply.hdr.kind_lo = 'g';
+	        reply.hdr.data_len = 12;
+
+		// YAAC asks for this.
+		// Fake it to keep application happy.
+
+	        reply.on_air_baud_rate = 0;
+		reply.traffic_level = 1;
+		reply.tx_delay = 0x19;
+		reply.tx_tail = 4;
+		reply.persist = 0xc8;
+		reply.slottime = 4;
+		reply.maxframe = 7;
+		reply.active_connections = 0;
+		reply.how_many_bytes = 1;
+
+		assert (sizeof(reply) == 48);
+
+	        if (debug_client) {
+	          debug_print (TO_CLIENT, &reply.hdr, sizeof(reply));
+	        }
+
+#if __WIN32__     
+	        send (client_sock, (char*)(&reply), sizeof(reply), 0);
+#else
+	        write (client_sock, &reply, sizeof(reply));
+#endif
+	      }
+	      break;
+
+
+	    case 'H':				/* Ask about recently heard stations. */
+
+	      {
+#if 0
+		struct {
+		  struct agwpe_s hdr;
+	 	  char info[100];
+		} reply;
+
+
+	        memset (&reply.hdr, 0, sizeof(reply.hdr));
+	        reply.hdr.kind_lo = 'H';
+
+		// TODO:  Implement properly.  
+
+	        reply.hdr.portx = cmd.hdr.portx
+
+	        strcpy (reply.hdr.call_from, "WB2OSZ-15");
+
+	        strcpy (agwpe_msg.data, ...);
+
+	        reply.hdr.data_len = strlen(reply.info);
+
+	        if (debug_client) {
+	          debug_print (TO_CLIENT, &reply.hdr, sizeof(reply.hdr) + reply.hdr.data_len);
+	        }
+
+#if __WIN32__     
+	        send (client_sock, &reply, sizeof(reply.hdr) + reply.hdr.data_len, 0);
+#else
+	        write (client_sock, &reply, sizeof(reply.hdr) + reply.hdr.data_len);
+#endif	      
+
+#endif
+	      }
+	      break;
+	    
+
+
+
+	    case 'k':				/* Ask to start receiving RAW AX25 frames */
+
+	      // Actually it is a toggle so we must be sure to clear it for a new connection.
+
+	      enable_send_raw_to_client = ! enable_send_raw_to_client;
+	      break;
+
+	    case 'm':				/* Ask to start receiving Monitor frames */
+
+	      // Actually it is a toggle so we must be sure to clear it for a new connection.
+
+	      enable_send_monitor_to_client = ! enable_send_monitor_to_client;
+	      break;
+
+
+	    case 'V':				/* Transmit UI data frame */
+	      {
+	      	// Data format is:
+	      	//	1 byte for number of digipeaters.
+	      	//	10 bytes for each digipeater.
+	      	//	data part of message.
+
+	      	char stemp[512];
+		char *p;
+		int ndigi;
+		int k;
+	      
+		packet_t pp;
+    		//unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
+    		//int flen;
+
+	      	strcpy (stemp, cmd.hdr.call_from);
+	      	strcat (stemp, ">");
+	      	strcat (stemp, cmd.hdr.call_to);
+
+		cmd.data[cmd.hdr.data_len] = '\0';
+		ndigi = cmd.data[0];
+		p = cmd.data + 1;
+
+		for (k=0; k<ndigi; k++) {
+		  strcat (stemp, ",");
+		  strcat (stemp, p);
+		  p += 10;
+	        }
+		strcat (stemp, ":");
+		strcat (stemp, p);
+
+	        //text_color_set(DW_COLOR_DEBUG);
+		//dw_printf ("Transmit '%s'\n", stemp);
+
+		pp = ax25_from_text (stemp, 1);
+
+
+		if (pp == NULL) {
+	          text_color_set(DW_COLOR_ERROR);
+		  dw_printf ("Failed to create frame from AGW 'V' message.\n");
+		}
+		else {
+
+		  /* This goes into the low priority queue because it is an original. */
+
+		  /* Note that the protocol has no way to set the "has been used" */
+		  /* bits in the digipeater fields. */
+
+		  /* This explains why the digipeating option is grayed out in */
+		  /* xastir when using the AGW interface.  */
+		  /* The current version uses only the 'V' message, not 'K' for transmitting. */
+
+		  tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp);
+
+		}
+	      }
+	      
+	      break;
+
+	    case 'K':				/* Transmit raw AX.25 frame */
+	      {
+	      	// Message contains:
+	      	//	port number for transmission.
+	      	//	data length
+	      	//	data which is raw ax.25 frame.
+		//		
+
+	      
+		packet_t pp;
+
+		pp = ax25_from_frame ((unsigned char *)cmd.data+1, cmd.hdr.data_len, -1);
+
+		if (pp == NULL) {
+	          text_color_set(DW_COLOR_ERROR);
+		  dw_printf ("Failed to create frame from AGW 'K' message.\n");
+		}
+		else {
+
+		  /* How can we determine if it is an original or repeated message? */
+		  /* If there is at least one digipeater in the frame, AND */
+		  /* that digipeater has been used, it should go out quickly thru */
+		  /* the high priority queue. */
+		  /* Otherwise, it is an original for the low priority queue. */
+
+		  if (ax25_get_num_repeaters(pp) >= 1 &&
+		      ax25_get_h(pp,AX25_REPEATER_1)) {
+		    tq_append (cmd.hdr.portx, TQ_PRIO_0_HI, pp);
+		  }
+		  else {
+		    tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp);
+		  }
+		}
+	      }
+	      
+	      break;
+
+	    case 'X':				/* Register CallSign  */
+
+	      /* Send success status. */
+
+	      {
+		struct {
+		  struct agwpe_s hdr;
+		  char data;
+		} reply;
+
+
+	        memset (&reply, 0, sizeof(reply));
+	        reply.hdr.kind_lo = 'X';
+		memcpy (reply.hdr.call_from, cmd.hdr.call_from, sizeof(reply.hdr.call_from));
+	        reply.hdr.data_len = 1;
+		reply.data = 1;		/* success */
+	
+		// Version 1.0.
+		// Previously used sizeof(reply) but compiler rounded it up to next byte boundary.
+		// That's why more cumbersome size expression is used.
+
+	        if (debug_client) {
+	          debug_print (TO_CLIENT, &reply.hdr, sizeof(reply.hdr) + sizeof(reply.data));
+	        }
+
+#if __WIN32__     
+	        send (client_sock, (char*)(&reply), sizeof(reply.hdr) + sizeof(reply.data), 0);
+#else
+	        write (client_sock, &reply, sizeof(reply.hdr) + sizeof(reply.data));
+#endif
+	      }
+	      break;
+
+	    case 'x':				/* Unregister CallSign  */
+	      /* No reponse is expected. */
+	      break;
+
+	    case 'C':				/* Connect, Start an AX.25 Connection  */
+	    case 'v':	      			/* Connect VIA, Start an AX.25 circuit thru digipeaters */
+	    case 'D': 				/* Send Connected Data */
+	    case 'd': 				/* Disconnect, Terminate an AX.25 Connection */
+
+	      // Version 1.0.  Better message instead of generic unexpected command.
+
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("\n");
+	      dw_printf ("Can't process command from AGW client app.\n");
+	      dw_printf ("Connected packet mode is not implemented.\n");
+
+	      break;
+
+#if 0
+	    case 'M': 				/* Send UNPROTO Information */
+
+		Not sure what we might want to do here.  
+		AGWterminal sends this for beacon or ask QRA.
+
+
+		<<< Send UNPROTO Information from AGWPE client application, total length = 253
+		        portx = 0, port_hi_reserved = 0
+		        kind_lo = 77 = 'M', kind_hi = 0
+		        call_from = "SV2AGW-1", call_to = "BEACON"
+		        data_len = 217, user_reserved = 588, data =
+		  000:  54 68 69 73 20 76 65 72 73 69 6f 6e 20 75 73 65  This version use
+		  010:  73 20 74 68 65 20 6e 65 77 20 41 47 57 20 50 61  s the new AGW Pa
+		  020:  63 6b 65 74 20 45 6e 67 69 6e 65 20 77 69 6e 73  cket Engine wins
+
+		<<< Send UNPROTO Information from AGWPE client application, total length = 37
+		        portx = 0, port_hi_reserved = 0
+		        kind_lo = 77 = 'M', kind_hi = 0
+		        call_from = "SV2AGW-1", call_to = "QRA"
+		        data_len = 1, user_reserved = 32218432, data =
+		  000:  0d                                               .
+
+	      break;
+
+#endif
+	    default:
+
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("--- Unexpected Command from application using AGW protocol:\n");
+	      debug_print (FROM_CLIENT, &cmd.hdr, sizeof(cmd.hdr) + cmd.hdr.data_len);
+
+	      break;
+	  }
+	
+
+	}
+
+}
+
+/* end server.c */
diff --git a/server.h b/server.h
new file mode 100755
index 0000000..2a75b18
--- /dev/null
+++ b/server.h
@@ -0,0 +1,19 @@
+
+/* 
+ * Name:	server.h
+ */
+
+
+#include "ax25_pad.h"		/* for packet_t */
+
+#include "config.h"
+
+
+void server_set_debug (int n);
+
+void server_init (struct misc_config_s *misc_config);
+
+void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf,  int flen);
+
+
+/* end server.h */
diff --git a/symbols-new.txt b/symbols-new.txt
new file mode 100755
index 0000000..6b81aee
--- /dev/null
+++ b/symbols-new.txt
@@ -0,0 +1,409 @@
+APRS SYMBOL OVERLAY and EXTENSION TABLES in APRS 1.2      20 May 2014
+---------------------------------------------------------------------
+
+BACKGROUND:  This file addresses new additions proposals (OVERLAYS) 
+to the APRS symbol set after 1 October 2007.  The master symbol 
+document remains on the www.aprs.org/symbols/symbolsX.txt page.
+
+
+NOTE:  There was confusion with different copies of this file on 
+different web pages and links.  THIS file is now assumed to be the
+CORRECT one.
+
+
+UPDATES/REVISIONS/CORRECTIONS:
+
+20 May 14  Changed Da to DSTAR (2700 of them) from Dutch Ares
+19 May 14  Added Submarine&torpedo to ships and lots of Aircraft
+           search for "(new may 2014)"
+07 Oct 13  Added new overlays to ships such as Jet Ski, Js
+           Added Ham Club symbol as a C overlay on House, C-
+19 Sep 11  Added T and 2 overlays for TX 1 and 2 hop IGates
+           Added overlays to (;) portable, to show event types
+23 Mar 11  Added Radiation Detector (RH)
+20 Apr 10  Byonics requested (BY)
+04 Jan 10  added #A to the table (correcting earlier omission)
+12 Oct 09  Added W0 for Yaesu WIRES nodes
+09 Apr 09  Changed APRStt symbol to overlayed BOX (#A)
+21 Aug 08  Added RFID R=, Stroller B], Radios#Y, & skull&Xbones (XH)
+27 Apr 08  Added some definitions of the numbered circle #0.          
+25 Mar 08  Added these new definitions of overlays:
+
+Original Alternate Symbol codes being modified for new Overlay Use:
+
+\A - (BOX symbol) APRStt(DTMF), RFID users, XO (OLPC) 
+\' - Was Crash Site.  Now expanded to be INCIDENT sites
+\% - is an overlayed Powerplant.  See definitions below
+\H - \H is HAZE but other H overlays are HAZARDs. WH is "H.Waste"
+\Y - Overlays for Radios and other APRS devices
+\k - Overlay Special vehicles.  A = ATV for example
+\u - Overlay Trucks.  "Tu" is a tanker. "Gu" is a gas truck, etc
+\< - Advisories may now have overlays
+\8 - Nodes with overlays. "G8" would be 802.11G
+\[ - \[ is wall cloud, but overlays are humans. S[ is a skier.
+\h - Buildings. \h is a Ham store, "Hh" is Home Depot, etc.
+
+Previous edition was 4 Oct 2007.
+
+In April 2007, a proposal to expand the use of overlay bytes for 
+the extension of the APRS symbol set was added to the draft APRS1.2 
+addendum web page.  The following document addresses that proposal:
+
+www.aprs.org/symbols/symbols-overlays.txt
+
+For details on Upgrading your symbol set, please see the background
+information on Symbols prepared by Stephen Smith, WA8LMF:
+
+www.aprs.org/symbols/symbols-background.txt
+
+CONSISTANCY:  Since the objective of APRS is consistent, reliable 
+communications at the local level, there has been a hesitance to 
+making significant changes to the APRS symbol set.  The Integrity 
+of APRS depends on everyone seeing the same information at the 
+same time.  Frequent changes to the symbol sets can actually
+undermine that integrity and operational utility of APRS and end up
+with worse outcomes due to miss-communications than the lack of any 
+particular symbol might suggest.
+
+OVERLAY HISTORY:  When the overlay symbol set was first defined for 
+the original APRS back in 1995, it had the potential to expand the 
+APRS symbol set from the 94 original primary symbols to a secondary 
+set that could each have as many as 36 diffeernt overlays on each of 
+those secondary symbols up to almost 3500 combinations.  But some 
+authors then could not easily implement these overlays, except by 
+one-by-one exceptions to their code.
+
+For this reason, a compromise was made with those authors and then
+eventually written into the APRS spec to limit overlays to only a 
+small subset of alternate symbols.  Those original overlayable
+alternate symbols were labeled with a "#" and called "numbered"
+symbols.   (UIview requires "No." in the symbols.ini file) 
+
+STATUS OF OVERLAYS 1 OCTOBER 2007:  the APRS symbol set only had a 
+few remaining unused symbol codes that had not yet been defined:
+
+OF THE 94 Primary Symbols.  The following were available:
+   10 symbols (/0 - /9) that mostly look like billiard balls now
+    4 symbols /D, /J, /Q, /z were undefined or TBD
+    2 were reserved
+
+OF THE 94 Alternate  Symbols.  The following were available:
+   3 undefined series \=, \Y, \Z which could do 36 overlays
+   8 series \1 through \8  that can support 36 overlays each
+   3 reserved series.
+
+ADDITIONAL OVERLAY PROPOSAL:  But any of the other 79 alternate 
+symbols could all have multiple (36) overlays if they can make sense 
+with the existing underlying basic symbol that we have been using for 
+that basic alternate symbol.  That is, any new definition of a 
+previously unused overlay character will have undefined results on all 
+prior APRS systems and should be used with caution.  But the symbol 
+set is extensible with these cautions.  (See the Proposal that would 
+expand the APRS symbol set to over 3200 at the bottom of this 
+document.)
+
+
+SYMBOL OVERLAY TABLES:  This document will keep track of all
+definitions of overlays on all ALTERNATE symbols.  Although these 
+overlays were originally intended to just overlay a displayable 
+single character on a basic symbol, there is no prohibition against
+taking the combination of a symbol and specific overlay, and then 
+letting that define a new graphic just for that combination.
+
+The following tables will attempt to keep track of these and
+any other useful generic applications of overlay characters.
+
+AIRCRAFT
+/^ = LARGE Aircraft
+\^ = top-view originally intended to point in direction of flight
+D^ = Drone   (new may 2014)
+E^ = Enemy aircraft (too bad I cant use the original Hostile)
+H^ = Hovercraft    (new may 2014)
+J^ = JET     (new may 2014)
+M^ = Missle   (new may 2014)
+V^ = Vertical takeoff   (new may 2014)
+
+ATM Machine or CURRENCY:  #$ 
+/$ = original primary Phone
+\$ = Bank or ATM (generic)
+U$ = US dollars
+L$ = Brittish Pound
+Y$ = Japanese Yen
+
+POWER PLANT: #%  
+/% = DX cluster  <= the original primary table definition
+C% = Coal
+G% = Geothermal
+H% = Hydroelectric
+N% = Nuclear
+S% = Solar
+T% = Turbine
+W% = Wind
+
+GATEWAYS: #&
+/& = HF Gateway  <= the original primary table definition
+I& = Igate Generic (please use more specific overlay)
+R& = Receive only IGate (do not send msgs back to RF)
+T& = TX igate with path set to 1 hop only)
+2& = TX igate with path set to 2 hops (not generally good idea)
+
+INCIDENT SITES: #'
+\' = Airplane Crash Site  <= the original primary deifinition
+A' = Automobile crash site
+H' = Hazardous incident
+M' = Multi-Vehicle crash site
+P' = Pileup
+T' = Truck wreck
+
+HUMAN SYMBOL: #[
+/[ = Human
+\[ = Wall Cloud (the original definition)
+B[ = Baby on board (stroller, pram etc)
+S[ = Skier      * <= Recommend Special Symbol
+R[ = Runner
+H[ = Hiker
+
+HOUSE: #-
+/- = House
+\- = (was HF)
+5- = 50 Hz mains power
+6- = 60 Hz mains power
+B- = Backup Battery Power
+C- = Club, as in Ham club
+E- = Emergency power
+G- = Geothermal
+H- = Hydro powered
+O- = Operator Present
+S- = Solar Powered
+W- = Wind powered
+
+NUMBERED CIRCLES: #0
+E0 = Echolink Node (E0)
+I0 = IRLP repeater (I0)
+S0 = Staging Area  (S0)
+W0 = WIRES (Yaesu VOIP)
+
+NETWORK NODES: #8
+88 = 802.11 network node (88)
+G8 = 802.11G  (G8)
+
+PORTABLE SYMBOL: #;
+/; = Portable operation (tent)
+\; = Park or Picnic
+F; = Field Day
+I; = Islands on the air
+S; = Summits on the air
+W; = WOTA
+
+ADVISORIES: #<  (new expansion possibilities)
+/< = motorcycle
+\< = Advisory (single gale flag)
+
+CARS: #>
+/> = normal car (side view)
+\> = Top view and symbol POINTS in direction of travel
+E> = Electric 
+H> = Hybrid
+S> = Solar powered
+V> = GM Volt
+
+BOX SYMBOL: #A (and other system inputted symbols)
+/A = Aid station
+\A = numbered box
+9A = Mobile DTMF user
+7A = HT DTMF user
+HA = House DTMF user
+EA = Echolink DTMF report
+IA = IRLP DTMF report
+RA = RFID report
+AA = AllStar DTMF report
+DA = D-Star report
+XA = OLPC Laptop XO
+etc
+
+EYEBALL and VISIBILITY  #E
+/E = Eyeball for special live events
+\E = (existing smoke) the symbol with no overlay
+HE = (H overlay) Haze
+SE = (S overlay) Smoke
+BE = (B overlay) Blowing Snow         was \B
+DE = (D overlay) blowing Dust or sand was \b
+FE = (F overlay) Fog                  was \{
+
+HAZARDS: #H
+/H = hotel
+\H = Haze
+RH = Radiation detector (new mar 2011)
+WH = Hazardous Waste
+XH = Skull&Crossbones
+
+RESTAURANTS: #R 
+\R = Restaurant (generic)
+7R = 7/11
+KR = KFC
+MR = McDonalds
+TR = Taco Bell
+
+RADIOS and APRS DEVICES: #Y
+/Y = Yacht  <= the original primary symbol
+\Y =        <= the original alternate was undefined
+AY = Alinco
+BY = Byonics
+IY = Icom
+KY = Kenwood       * <= Recommend special symbol
+YY = Yaesu/Standard* <= Recommend special symbol
+
+GPS devices: #\
+/\ = Triangle DF primary symbol
+\\ = was undefined alternate symbol
+A\ = Avmap G5      * <= Recommend special symbol
+
+ARRL or DIAMOND: #a
+/a = Ambulance
+Aa = ARES
+Da = DSTAR (had been ARES Dutch)
+Ga = RSGB Radio Society of Great Brittan
+Ra = RACES
+Sa = SATERN Salvation Army
+Wa = WinLink
+
+CIVIL DEFENSE or TRIANGLE: #c
+/c = Incident Command Post
+\c = Civil Defense
+Rc = RACES
+Sc = SATERN mobile canteen
+
+BUILDINGS: #h
+/h = Hospital
+\h = Ham Store       ** <= now used for HAMFESTS
+Hh = Home Dept etc..
+
+SPECIAL VEHICLES: #k
+/k = truck
+\k = SUV
+4k = 4x4
+Ak = ATV (all terrain vehicle)
+
+SHIPS: #s
+/s = Power boat (ship) side view
+\s = Overlay Boat (Top view)
+Cs = receive as Canoe but still transmit canoe as /C
+Js = Jet Ski
+Ks = Kayak
+Hs = Hovercraft (new may 2014)
+Ts = Torpedo   (new may 2014)
+Us = sUbmarine U-boat   (new may 2014)
+
+TRUCKS: #u
+/u = Truck (18 wheeler)
+\u = truck with overlay
+Gu = Gas
+Tu = Tanker
+Cu = Chlorine Tanker
+Hu = Hazardous
+
+
+Anyone can use any overlay on any of the overlayable symbols for any
+special purpose.  We are not trying to document all possible such
+overlays.  The purpose of this document is to help keep a list of 
+more common such definitions that have more universal use and for
+which multiple definitions would lead to confusion.
+
+Future APRS code writers should be aware of where we are going:
+
+********************************************************************
+PROPOSAL TO ASSIGN MANY MORE BLOCKS OF SYMBOL SETS        April 2007
+--------------------------------------------------------------------
+www.aprs.org/symbols/symbols-overlays.txt
+
+In the initiative to upgrade APRS symbols, we are wasting some
+very valuable OVERLAYABLE symbol subgroups with a few nailed
+down legacy weather symbols.  We are proposing to consolidate
+some of these Weather symbols to open up much more space.  Since
+this is the first time we have considered actually CHANGING some
+symbol definitions, this can cause problems out there for some
+users of some legacy systems.
+
+That is why I am posting this plan to the APRS community.  If we
+do this, XASTIR and UIVIEW will be able to download new symbol
+definitions.  But some legacy clients that do not operate from
+external symbol files will show the wrong symbols for these. If
+users of those systems forsee some serious problems with the
+re-arrangement of these symbols, let us know the specific impact
+and your ideas for a workaround.. 
+
+The symbols that would be impacted are as follows:
+
+First, consolidate all of the visibility symbols into the old
+SMOKE symbol and change its meaning to "VISIBILITY", and then
+differentiate them with the overlay characters.  
+
+"\E"  (existing smoke) the symbol with no overlay
+"HE"  (an H overlay) will mean Haze
+"SE"  (an S overlay) will mean Smoke
+"BE"  (a  B overlay) will mean Blowing Snow         was \B
+"DE"  (a  D overlay) will mean blowing Dust or sand was \b
+"FE"  (an F overlay) will mean Fog                  was \{
+
+Another category is to expand the RAIN symbol to make it kinda
+like lots of angled dots coming from the sky, but with an open
+center so that we can use overlays for a number of common
+PRECIPITATIONS.  The consolidations would be:
+
+"\`" (existing Rain) would be the symbol with no overlay
+"R`" (an R overlay) would mean Rain
+"F`" (an F overlay) would mean Freezing Rain  was \F
+"H`" (an H overlay) would mean Hail           was\:
+"D`" (an D overlay) would mean Drizzle        was \D
+"E`" (an E overlay) would mean slEEt          was \e
+"S`" (an S overlay) would mean Snow           was \*
+Etc. and other particulates coming from the sky
+
+Next, I propose expanding the existing RAIN SHOWER "\I" symbol
+to look like some kind of cloud symbol with specks in it that
+can be overlayed. (It needs to look different from the next
+CLOUD symbol). It can then consolidate these symbols:
+
+"RI" (an R overlay) would mean Rain Shower
+"SI" (an S overlay) would mean Snow shower    was \G
+"LI" (an L overlay) would mean Lightening     was \J
+Etc. and other things related to clouds
+
+Next, I propose expanding the existing CLOUD symbol to allow
+definition of any number of different types of cloud.  This
+needs to also look like a cloud but a different shape and allow
+for overlays  (Maybe this cloud is clear):
+
+"\(" is the existing cloud symbol (would have no overlay)
+"P(" with P overlay would mean partly cloudy        was \p
+"W(" with W overlay would be a wall cloud           was \[
+"F(" would be Funnel cloud, but the original "\f" will also be
+retained for now
+
+All of these initiative will free up a lot of overlayable symbol 
+GROUPS each of which can suport up to 36 different overlays in 
+each group for the future:
+
+#H for 36 new Hazards (was only Hail)
+#[ for 36 new human symbols (was only Wall Cloud)
+#\ for 36 new GPS or navigation equipment
+#B TBD. \B was only blowing snow         now is BE
+#b TBD. \b was only blowing dust/sand    now is DE
+#{ TBD. \{ was only fog                  now is FE
+#* TBD. \* was snow only                 now is S`
+#: TBD. \: was hail only                 now is H`
+#D TBD. \D was drizzle only              now is D`
+#F TBD. \F was freezing rain only        now is F`
+#e TBD. \e was sleet only                now is E`
+#G TBD. \G was only Snow shower          now is SI
+#J TBD. \J was only Lightening           now is LI
+#p TBD. \p was only partly cloudy        now is P(
+
+Of course future code can fully draw each of these overlays as
+distinct special symbols in any way they want. 
+
+I especially want to hear from Dale Hugley who
+is a resource for weather, and Stephen Smith who will have to
+draw them for Uiview. And others with a stake in this...
+
+Bob Bruninga, WB4APR
diff --git a/symbols.c b/symbols.c
new file mode 100755
index 0000000..1ba1773
--- /dev/null
+++ b/symbols.c
@@ -0,0 +1,898 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012,2013,2014  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * File:	symbols.c
+ *
+ * Purpose:	Functions related to the APRS symbols
+ *
+ *------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>	
+#include <string.h>	
+#include <ctype.h>	
+#include "textcolor.h"
+#include "symbols.h"
+
+//#if __WIN32__
+	char *strcasestr(const char *S, const char *FIND);
+//#endif
+
+/*
+ * APRS symbol tables.
+ *
+ * Derived from http://www.aprs.org/symbols/symbolsX.txt
+ * version of 20 Oct 2009.
+ */
+
+/*
+ * Primary symbol table.
+ */
+
+#define SYMTAB_SIZE 95
+
+
+static const struct {
+		char xy[3];
+		char *description;
+	} primary_symtab[SYMTAB_SIZE] = {
+
+	/*     00  */	{ "~~", "--no-symbol--" },
+	/*  !  01  */	{ "BB", "Police, Sheriff" },
+	/*  "  02  */	{ "BC", "reserved  (was rain)" },
+	/*  #  03  */	{ "BD", "DIGI (white center)" },
+	/*  $  04  */	{ "BE", "PHONE" },
+	/*  %  05  */	{ "BF", "DX CLUSTER" },
+	/*  &  06  */	{ "BG", "HF GATEway" },
+	/*  '  07  */	{ "BH", "Small AIRCRAFT" },
+	/*  (  08  */	{ "BI", "Mobile Satellite Station" },
+	/*  )  09  */	{ "BJ", "Wheelchair (handicapped)" },
+	/*  *  10  */	{ "BK", "SnowMobile" },
+	/*  +  11  */	{ "BL", "Red Cross" },
+	/*  ,  12  */	{ "BM", "Boy Scouts" },
+	/*  -  13  */	{ "BN", "House QTH (VHF)" },
+	/*  .  14  */	{ "BO", "X" },
+	/*  /  15  */	{ "BP", "Red Dot" },
+	/*  0  16  */	{ "P0", "# circle (obsolete)" },
+	/*  1  17  */	{ "P1", "TBD" },
+	/*  2  18  */	{ "P2", "TBD" },
+	/*  3  19  */	{ "P3", "TBD" },
+	/*  4  20  */	{ "P4", "TBD" },
+	/*  5  21  */	{ "P5", "TBD" },
+	/*  6  22  */	{ "P6", "TBD" },
+	/*  7  23  */	{ "P7", "TBD" },
+	/*  8  24  */	{ "P8", "TBD" },
+	/*  9  25  */	{ "P9", "TBD" },
+	/*  :  26  */	{ "MR", "FIRE" },
+	/*  ;  27  */	{ "MS", "Campground (Portable ops)" },
+	/*  <  28  */	{ "MT", "Motorcycle" },
+	/*  =  29  */	{ "MU", "RAILROAD ENGINE" },
+	/*  >  30  */	{ "MV", "CAR" },
+	/*  ?  31  */	{ "MW", "SERVER for Files" },
+	/*  @  32  */	{ "MX", "HC FUTURE predict (dot)" },
+	/*  A  33  */	{ "PA", "Aid Station" },
+	/*  B  34  */	{ "PB", "BBS or PBBS" },
+	/*  C  35  */	{ "PC", "Canoe" },
+	/*  D  36  */	{ "PD", "" },
+	/*  E  37  */	{ "PE", "EYEBALL (Eye catcher!)" },
+	/*  F  38  */	{ "PF", "Farm Vehicle (tractor)" },
+	/*  G  39  */	{ "PG", "Grid Square (6 digit)" },
+	/*  H  40  */	{ "PH", "HOTEL (blue bed symbol)" },
+	/*  I  41  */	{ "PI", "TcpIp on air network stn" },
+	/*  J  42  */	{ "PJ", "" },
+	/*  K  43  */	{ "PK", "School" },
+	/*  L  44  */	{ "PL", "PC user" },
+	/*  M  45  */	{ "PM", "MacAPRS" },
+	/*  N  46  */	{ "PN", "NTS Station" },
+	/*  O  47  */	{ "PO", "BALLOON" },
+	/*  P  48  */	{ "PP", "Police" },
+	/*  Q  49  */	{ "PQ", "TBD" },
+	/*  R  50  */	{ "PR", "REC. VEHICLE" },
+	/*  S  51  */	{ "PS", "SHUTTLE" },
+	/*  T  52  */	{ "PT", "SSTV" },
+	/*  U  53  */	{ "PU", "BUS" },
+	/*  V  54  */	{ "PV", "ATV" },
+	/*  W  55  */	{ "PW", "National WX Service Site" },
+	/*  X  56  */	{ "PX", "HELO" },
+	/*  Y  57  */	{ "PY", "YACHT (sail)" },
+	/*  Z  58  */	{ "PZ", "WinAPRS" },
+	/*  [  59  */	{ "HS", "Human/Person (HT)" },
+	/*  \  60  */	{ "HT", "TRIANGLE(DF station)" },
+	/*  ]  61  */	{ "HU", "MAIL/PostOffice(was PBBS)" },
+	/*  ^  62  */	{ "HV", "LARGE AIRCRAFT" },
+	/*  _  63  */	{ "HW", "WEATHER Station (blue)" },
+	/*  `  64  */	{ "HX", "Dish Antenna" },
+	/*  a  65  */	{ "LA", "AMBULANCE" },
+	/*  b  66  */	{ "LB", "BIKE" },
+	/*  c  67  */	{ "LC", "Incident Command Post" },
+	/*  d  68  */	{ "LD", "Fire dept" },
+	/*  e  69  */	{ "LE", "HORSE (equestrian)" },
+	/*  f  70  */	{ "LF", "FIRE TRUCK" },
+	/*  g  71  */	{ "LG", "Glider" },
+	/*  h  72  */	{ "LH", "HOSPITAL" },
+	/*  i  73  */	{ "LI", "IOTA (islands on the air)" },
+	/*  j  74  */	{ "LJ", "JEEP" },
+	/*  k  75  */	{ "LK", "TRUCK" },
+	/*  l  76  */	{ "LL", "Laptop" },
+	/*  m  77  */	{ "LM", "Mic-E Repeater" },
+	/*  n  78  */	{ "LN", "Node (black bulls-eye)" },
+	/*  o  79  */	{ "LO", "EOC" },
+	/*  p  80  */	{ "LP", "ROVER (puppy, or dog)" },
+	/*  q  81  */	{ "LQ", "GRID SQ shown above 128 m" },
+	/*  r  82  */	{ "LR", "Repeater" },
+	/*  s  83  */	{ "LS", "SHIP (pwr boat)" },
+	/*  t  84  */	{ "LT", "TRUCK STOP" },
+	/*  u  85  */	{ "LU", "TRUCK (18 wheeler)" },
+	/*  v  86  */	{ "LV", "VAN" },
+	/*  w  87  */	{ "LW", "WATER station" },
+	/*  x  88  */	{ "LX", "xAPRS (Unix)" },
+	/*  y  89  */	{ "LY", "YAGI @ QTH" },
+	/*  z  90  */	{ "LZ", "TBD" },
+	/*  {  91  */	{ "J1", "" },
+	/*  |  92  */	{ "J2", "TNC Stream Switch" },
+	/*  }  93  */	{ "J3", "" },
+	/*  ~  94  */	{ "J3", "TNC Stream Switch" } };
+
+/*
+ * Alternate symbol table.
+ */
+
+static const struct {
+		char xy[3];
+		char *description;
+	} alternate_symtab[SYMTAB_SIZE] = {
+
+	/*     00  */	{ "~~", "--no-symbol--" },
+	/*  !  01  */	{ "OB", "EMERGENCY (!)" },
+	/*  "  02  */	{ "OC", "reserved" },
+	/*  #  03  */	{ "OD", "OVERLAY DIGI (green star)" },
+	/*  $  04  */	{ "OE", "Bank or ATM  (green box)" },
+	/*  %  05  */	{ "OF", "Power Plant with overlay" },
+	/*  &  06  */	{ "OG", "I=Igte IGate R=RX T=1hopTX 2=2hopTX" },
+	/*  '  07  */	{ "OH", "Crash (& now Incident sites)" },
+	/*  (  08  */	{ "OI", "CLOUDY (other clouds w ovrly)" },
+	/*  )  09  */	{ "OJ", "Firenet MEO, MODIS Earth Obs." },
+	/*  *  10  */	{ "OK", "SNOW (& future ovrly codes)" },
+	/*  +  11  */	{ "OL", "Church" },
+	/*  ,  12  */	{ "OM", "Girl Scouts" },
+	/*  -  13  */	{ "ON", "House (H=HF) (O = Op Present)" },
+	/*  .  14  */	{ "OO", "Ambiguous (Big Question mark)" },
+	/*  /  15  */	{ "OP", "Waypoint Destination" },
+	/*  0  16  */	{ "A0", "CIRCLE (E/I/W=IRLP/Echolink/WIRES)" },
+	/*  1  17  */	{ "A1", "" },
+	/*  2  18  */	{ "A2", "" },
+	/*  3  19  */	{ "A3", "" },
+	/*  4  20  */	{ "A4", "" },
+	/*  5  21  */	{ "A5", "" },
+	/*  6  22  */	{ "A6", "" },
+	/*  7  23  */	{ "A7", "" },
+	/*  8  24  */	{ "A8", "802.11 or other network node" },
+	/*  9  25  */	{ "A9", "Gas Station (blue pump)" },
+	/*  :  26  */	{ "NR", "Hail (& future ovrly codes)" },
+	/*  ;  27  */	{ "NS", "Park/Picnic area" },
+	/*  <  28  */	{ "NT", "ADVISORY (one WX flag)" },
+	/*  =  29  */	{ "NU", "APRStt Touchtone (DTMF users)" },
+	/*  >  30  */	{ "NV", "OVERLAYED CAR" },
+	/*  ?  31  */	{ "NW", "INFO Kiosk  (Blue box with ?)" },
+	/*  @  32  */	{ "NX", "HURICANE/Trop-Storm" },
+	/*  A  33  */	{ "AA", "overlayBOX DTMF & RFID & XO" },
+	/*  B  34  */	{ "AB", "Blwng Snow (& future codes)" },
+	/*  C  35  */	{ "AC", "Coast Guard" },
+	/*  D  36  */	{ "AD", "Drizzle (proposed APRStt)" },
+	/*  E  37  */	{ "AE", "Smoke (& other vis codes)" },
+	/*  F  38  */	{ "AF", "Freezng rain (&future codes)" },
+	/*  G  39  */	{ "AG", "Snow Shwr (& future ovrlys)" },
+	/*  H  40  */	{ "AH", "Haze (& Overlay Hazards)" },
+	/*  I  41  */	{ "AI", "Rain Shower" },
+	/*  J  42  */	{ "AJ", "Lightening (& future ovrlys)" },
+	/*  K  43  */	{ "AK", "Kenwood HT (W)" },
+	/*  L  44  */	{ "AL", "Lighthouse" },
+	/*  M  45  */	{ "AM", "MARS (A=Army,N=Navy,F=AF)" },
+	/*  N  46  */	{ "AN", "Navigation Buoy" },
+	/*  O  47  */	{ "AO", "Rocket" },
+	/*  P  48  */	{ "AP", "Parking" },
+	/*  Q  49  */	{ "AQ", "QUAKE" },
+	/*  R  50  */	{ "AR", "Restaurant" },
+	/*  S  51  */	{ "AS", "Satellite/Pacsat" },
+	/*  T  52  */	{ "AT", "Thunderstorm" },
+	/*  U  53  */	{ "AU", "SUNNY" },
+	/*  V  54  */	{ "AV", "VORTAC Nav Aid" },
+	/*  W  55  */	{ "AW", "# NWS site (NWS options)" },
+	/*  X  56  */	{ "AX", "Pharmacy Rx (Apothicary)" },
+	/*  Y  57  */	{ "AY", "Radios and devices" },
+	/*  Z  58  */	{ "AZ", "" },
+	/*  [  59  */	{ "DS", "W.Cloud (& humans w Ovrly)" },
+	/*  \  60  */	{ "DT", "New overlayable GPS symbol" },
+	/*  ]  61  */	{ "DU", "" },
+	/*  ^  62  */	{ "DV", "# Aircraft (shows heading)" },
+	/*  _  63  */	{ "DW", "# WX site (green digi)" },
+	/*  `  64  */	{ "DX", "Rain (all types w ovrly)" },
+	/*  a  65  */	{ "SA", "ARRL, ARES, WinLINK" },
+	/*  b  66  */	{ "SB", "Blwng Dst/Snd (& others)" },
+	/*  c  67  */	{ "SC", "CD triangle RACES/SATERN/etc" },
+	/*  d  68  */	{ "SD", "DX spot by callsign" },
+	/*  e  69  */	{ "SE", "Sleet (& future ovrly codes)" },
+	/*  f  70  */	{ "SF", "Funnel Cloud" },
+	/*  g  71  */	{ "SG", "Gale Flags" },
+	/*  h  72  */	{ "SH", "Store. or HAMFST Hh=HAM store" },
+	/*  i  73  */	{ "SI", "BOX or points of Interest" },
+	/*  j  74  */	{ "SJ", "WorkZone (Steam Shovel)" },
+	/*  k  75  */	{ "SK", "Special Vehicle SUV,ATV,4x4" },
+	/*  l  76  */	{ "SL", "Areas      (box,circles,etc)" },
+	/*  m  77  */	{ "SM", "Value Sign (3 digit display)" },
+	/*  n  78  */	{ "SN", "OVERLAY TRIANGLE" },
+	/*  o  79  */	{ "SO", "small circle" },
+	/*  p  80  */	{ "SP", "Prtly Cldy (& future ovrlys)" },
+	/*  q  81  */	{ "SQ", "" },
+	/*  r  82  */	{ "SR", "Restrooms" },
+	/*  s  83  */	{ "SS", "OVERLAY SHIP/boat (top view)" },
+	/*  t  84  */	{ "ST", "Tornado" },
+	/*  u  85  */	{ "SU", "OVERLAYED TRUCK" },
+	/*  v  86  */	{ "SV", "OVERLAYED Van" },
+	/*  w  87  */	{ "SW", "Flooding" },
+	/*  x  88  */	{ "SX", "Wreck or Obstruction ->X<-" },
+	/*  y  89  */	{ "SY", "Skywarn" },
+	/*  z  90  */	{ "SZ", "OVERLAYED Shelter" },
+	/*  {  91  */	{ "Q1", "Fog (& future ovrly codes)" },
+	/*  |  92  */	{ "Q2", "TNC Stream Switch" },
+	/*  }  93  */	{ "Q3", "" },
+	/*  ~  94  */	{ "Q4", "TNC Stream Switch" } };
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	symbols_init
+ *
+ * Purpose:	Initialization for functions related to symbols.
+ *
+ * Inputs:	
+ *
+ * Global output:
+ *		new_sym_ptr
+ *		new_sym_size
+ *		new_sym_len
+ *
+ * Description:	The primary and alternate symbol tables are constant
+ *		so they are hardcoded.
+ *		However the "new" sysmbols, which give new meanings to
+ *		overlayed symbols, are always evolving.
+ *		For maximum flexibility, we will read the
+ *		data file at run time rather than compiling it in.
+ *
+ *		For the most recent version, download from:
+ *
+ *		http://www.aprs.org/symbols/symbols-new.txt
+ *
+ *		Windows version:  File must be in current working directory.
+ *
+ *		Linux version: Search order is current working directory
+ *			then /usr/share/direwolf directory.
+ *
+ *------------------------------------------------------------------*/
+
+#define NEW_SYM_INIT_SIZE 20
+#define NEW_SYM_DESC_LEN 29
+
+typedef struct new_sym_s {
+	char overlay;
+	char symbol;
+	char description[NEW_SYM_DESC_LEN+1];
+} new_sym_t;
+
+static new_sym_t *new_sym_ptr = NULL;	/* Dynamically allocated array. */
+static int new_sym_size = 0;		/* Number of elements allocated. */
+static int new_sym_len = 0;			/* Number of elements used. */
+
+
+void symbols_init (void)
+{
+	FILE *fp;
+	struct {
+	  char overlay;
+	  char symbol;
+	  char sp1;
+	  char equal;
+	  char sp2;
+	  char description[150];
+	} stuff;
+	int j;
+
+#define GOOD_LINE(x) ((x.overlay == '/' || x.overlay == '\\' || isupper(x.overlay) || isdigit(x.overlay)) \
+			&& x.symbol >= '!' && x.symbol <= '~' \
+			&& x.sp1 == ' ' && x.equal == '=' && x.sp2 == ' ')
+
+
+	if (new_sym_ptr != NULL) {
+	  return;			/* was called already. */
+	}
+
+// If search strategy changes, be sure to keep decode_tocall in sync.
+
+
+	fp = fopen("symbols-new.txt", "r");
+#ifndef __WIN32__
+	if (fp == NULL) {
+	  fp = fopen("/usr/share/direwolf/symbols-new.txt", "r");
+	}
+#endif
+	if (fp == NULL) {
+
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Warning: Could not open 'symbols-new.txt'.\n");
+	  dw_printf ("The \"new\" overlayed character information will not be available.\n");
+
+	  new_sym_size = 1;	
+	  new_sym_ptr = calloc(new_sym_size, sizeof(new_sym_t));  /* Don't try again. */
+	  new_sym_len = 0;	
+	  return;
+	}
+
+/*
+ * Count number of interesting lines and allocate storage.
+ */
+	while (fgets((char*)(&stuff), sizeof(stuff), fp) != NULL) {
+	  if (GOOD_LINE(stuff)) {
+	    new_sym_size++;
+	  }
+	}
+
+	new_sym_ptr = calloc(new_sym_size, sizeof(new_sym_t));
+
+/*
+ * Rewind, read again, and save contents of interesting lines. 
+ */
+	rewind (fp);
+
+	while (fgets((char*)(&stuff), sizeof(stuff), fp) != NULL) {
+
+	  if (GOOD_LINE(stuff)) {
+	    for (j = strlen(stuff.description) - 1; j>=0 && stuff.description[j] <= ' '; j--) {
+	      stuff.description[j] = '\0';
+	    }
+	    new_sym_ptr[new_sym_len].overlay = stuff.overlay;
+	    new_sym_ptr[new_sym_len].symbol = stuff.symbol;
+	    strncpy(new_sym_ptr[new_sym_len].description, stuff.description, NEW_SYM_DESC_LEN);
+	    new_sym_len++;
+	  }
+	}
+	fclose (fp);
+
+	assert (new_sym_len == new_sym_size);
+
+#if 0
+	for (j=0; j<new_sym_len; j++) {
+	  dw_printf ("%02d: %c %c '%s'\n", j, new_sym_ptr[j].overlay,
+		new_sym_ptr[j].symbol, new_sym_ptr[j].description);
+	}
+#endif
+
+} /* end symbols_init */
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	symbols_from_dest_or_src
+ *
+ * Purpose:	Extract symbol from destination or source.
+ *
+ * Inputs:	dti	- Data type indicator.
+ *
+ *		src	- Source address with SSID.
+ *		
+ *		dest	- Destination address.
+ *			  Don't care if SSID is present or not.
+ *
+ * Outputs:	*symtab
+ *		*symbol
+ *
+ * Description:	There are 3 different ways to specify the symbol,
+ *		in this order of precedence:
+ *	
+ *		- Information field.  This was done already in the
+ *		  separate decoders for different message types.
+ *
+ *		If not set already,
+ *
+ *		- The destination address if it has certain formats
+ *		  starting with GPS, SPC, or SYM which are equivalent
+ *		  for our purposes.
+ *		  (Not for MIC-E where destination has a special use.)
+ *
+ *		If all else fails,
+ *
+ *		- SSID of the source address.
+ *
+ *
+ *------------------------------------------------------------------*/
+
+const static char ssid_to_sym[16] = {
+	  ' ',	/* 0 - No icon. */
+	  'a',	/* 1 - Ambulance */
+	  'U',	/* 2 - Bus */
+	  'f',	/* 3 - Fire Truck */
+	  'b',	/* 4 - Bicycle */
+	  'Y',	/* 5 - Yacht */
+	  'X',	/* 6 - Helicopter */
+	  '\'',	/* 7 - Small Aircraft */
+	  's',	/* 8 - Ship */
+	  '>',	/* 9 - Car */
+	  '<',	/* 10 - Motorcycle */
+	  'O',	/* 11 - Ballon */
+	  'j',	/* 12 - Jeep */
+	  'R',	/* 13 - Recreational Vehicle */
+	  'k',	/* 14 - Truck */
+	  'v' 	/* 15 - Van */
+	};
+
+void symbols_from_dest_or_src (char dti, char *src, char *dest, char *symtab, char *symbol)
+{
+	char *p;
+
+
+/*
+ * This part does not apply to MIC-E format because the destination
+ * is used to encode latitude and other information.
+ */
+	if (dti != '\'' && dti != '`') {
+
+/* 
+ * For GPSCnn, nn is the index into the primary symbol table.
+ */
+
+	  if (strncmp(dest, "GPSC", 4) == 0)
+	  {
+	    int nn;
+	  
+	    nn = atoi(dest+4);
+	    if (nn >= 1 && nn <= 94) {
+	      *symtab = '/';		/* Primary */
+	      *symbol = ' ' + nn;	
+	      return;
+	    }
+	  }
+
+/* 
+ * For GPSEnn, nn is the index into the primary symbol table.
+ */
+
+	  if (strncmp(dest, "GPSE", 4) == 0)
+	  {
+	    int nn;
+	  
+	    nn = atoi(dest+4);
+	    if (nn >= 1 && nn <= 94) {
+	      *symtab = '\\';		/* Alternate. */
+	      *symbol = ' ' + nn;	
+	      return;
+	    }
+	  }
+
+/*
+ * For GPSxy or SPCxy or SYMxy, look up xy in the translation tables.
+ * First search the primary table.
+ */
+
+	  if (strncmp(dest, "GPS", 3) == 0 ||
+	      strncmp(dest, "SPC", 3) == 0 ||
+	      strncmp(dest, "SYM", 3) == 0) 
+	  {
+	    char xy[3];
+	    int nn;
+	  
+	    xy[0] = dest[3];
+	    xy[1] = dest[4];
+	    xy[2] = '\0';
+
+	    for (nn = 1; nn <= 94; nn++) {
+	      if (strcmp(xy, primary_symtab[nn].xy) == 0) {
+	        *symtab = '/';		/* Primary. */
+	        *symbol = ' ' + nn;
+	        return;
+	      }
+	    }
+	  }			
+
+/*
+ * Next, search the alternate table.
+ * This time, we can have the format ...xyz, where z is an overlay character.
+ * Only upper case letters and digits are valid overlay characters.
+ */
+
+	  if (strncmp(dest, "GPS", 3) == 0 ||
+	      strncmp(dest, "SPC", 3) == 0 ||
+	      strncmp(dest, "SYM", 3) == 0) 
+	  {
+	    char xy[3];
+	    char z;
+	    int nn;
+	  
+	    xy[0] = dest[3];
+	    xy[1] = dest[4];
+	    xy[2] = '\0';
+	    z = dest[5];
+
+	    for (nn = 1; nn <= 94; nn++) {
+	      if (strcmp(xy, alternate_symtab[nn].xy) == 0) {
+	        *symtab = '\\';		/* Alternate. */
+	        *symbol = ' ' + nn;
+		if (isupper((int)z) || isdigit((int)z)) {
+	          *symtab = z;
+	        }
+	        return;
+	      }
+	    }
+	  }
+
+	}  /* end not MIC-E */
+
+/*
+ * When all else fails, use source SSID.
+ */
+
+	p = strchr (src, '-');
+	if (p != NULL) 
+	{
+	  int ssid;
+
+	  ssid = atoi(p+1);
+	  if (ssid >= 1 && ssid <= 15) {
+	    *symtab = '/';		/* All in Primary table. */
+	    *symbol = ssid_to_sym[ssid];
+	    return;
+	  }
+	}
+
+} /* symbols_from_dest_or_src */
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	symbols_into_dest
+ *
+ * Purpose:	Encode symbol for destination field.
+ *
+ * Inputs:	symtab		/, \, 0-9, A-Z
+ *		symbol		any printable character ! to ~ 
+ *
+ * Outputs:	dest		6 character "destination" of the forms 
+ *					GPSCnn  - primary table.
+ *					GPSEnn  - alternate table.
+ *					GPSxyz  - alternate with overlay.
+ *
+ * Returns:	0 for success, 1 for error.
+ *
+ *------------------------------------------------------------------*/
+
+int symbols_into_dest (char symtab, char symbol, char *dest)
+{
+
+	if (symbol >= '!' && symbol <= '~' && symtab == '/') {
+	  
+	  /* Primary Symbol table. */
+	  sprintf (dest, "GPSC%02d", symbol - ' ');
+	  return (0);
+	}
+	else if (symbol >= '!' && symbol <= '~' && symtab == '\\') {
+	  
+	  /* Alternate Symbol table. */
+	  sprintf (dest, "GPSE%02d", symbol - ' ');
+	  return (0);
+	}
+	else if (symbol >= '!' && symbol <= '~' && (isupper(symtab) || isdigit(symtab))) {
+
+	  /* Alternate Symbol table with overlay. */
+	  sprintf (dest, "GPS%s%c", alternate_symtab[symbol - ' '].xy, symtab);
+	  return (0);
+	}
+
+
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf ("Could not convert symbol \"%c%c\" to GPSxyz destination format.\n",
+			symtab, symbol);
+
+	strcpy (dest, "GPS???");	/* Error. */
+	return (1);
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	symbols_get_description
+ *
+ * Purpose:	Get description for given symbol table/code/overlay.
+ *
+ * Inputs:	symtab		/, \, 0-9, A-Z
+ *		symbol		any printable character ! to ~ 
+ *
+ * Outputs:	description	Text description.
+ *				"--no-symbol--"  if error.
+ *
+ *
+ * Description:	This is used for the monitoring and the 
+ *		decode_aprs utility.
+ *
+ *------------------------------------------------------------------*/
+
+void symbols_get_description (char symtab, char symbol, char *description)
+{
+	char tmp2[2];
+	int j;
+
+	symbols_init();
+
+
+// The symbol table identifier should be 
+//	/	for symbol from primary table
+//	\	for symbol from alternate table
+//	0-9,A-Z	for alternate symbol table with overlay character
+
+	if (symtab != '/' &&
+	    symtab != '\\' &&
+	    ! isdigit(symtab) &&
+	    ! isupper(symtab)) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Symbol table identifier is not '/' (primary), '\\' (alternate), or valid overlay character.\n");
+	
+	  /* Possibilities: */
+	  /* Select primary table and keep going, or */
+	  /* report no symbol.  It IS an error. */
+	  /* We do the latter. */
+
+	  symbol = ' ';
+	  strcpy (description, primary_symtab[symbol-' '].description);
+	  return;
+	}
+
+// Bounds check before using symbol as index into table.
+
+	if (symbol < ' ' || symbol > '~') {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Symbol code is not a printable character.\n");
+	  symbol = ' ';		/* Avoid subscript out of bounds. */	  
+	}
+
+// First try to match with the "new" symbols.
+
+	for (j=0; j<new_sym_len; j++) {
+	  if (symtab == new_sym_ptr[j].overlay && symbol == new_sym_ptr[j].symbol) {
+	    strcpy (description, new_sym_ptr[j].description);
+	    return;
+	  }
+	}  
+
+// Otherwise use the original symbol tables.
+
+	if (symtab == '/') {
+	  strcpy (description, primary_symtab[symbol-' '].description);
+	}
+	else {
+	  strcpy (description, alternate_symtab[symbol-' '].description);
+	  if (symtab != '\\') {
+	    strcat (description, " w/overlay ");
+	    tmp2[0] = symtab;
+	    tmp2[1] = '\0';
+	    strcat (description, tmp2);
+	  }
+	}
+
+} /* end symbols_get_description */
+
+
+/*------------------------------------------------------------------
+ *
+ * Function:	symbols_code_from_description
+ *
+ * Purpose:	Find a suitable table/symbol based on given description.
+ *
+ * Inputs:	overlay		Explicit overlay or space.
+ *		description	Substring of one of the descriptions.
+ *				Example: dog
+ *
+ * Outputs:	symtab		/, \, 0-9, A-Z
+ *		symbol		any printable character ! to ~ 
+ *
+ * Returns:	1 for success, 0 for failure.
+ *
+ *------------------------------------------------------------------*/
+
+int symbols_code_from_description (char overlay, char *description, char *symtab, char *symbol)
+{
+	int j;
+
+	symbols_init();
+
+/*
+ * If user specified a particular overlay (i.e. for config file BEACON), 
+ * first try the alternate symbol table.
+ * If that fails should we give up or ignore the overlay and keep trying?
+ */
+
+	if (isupper(overlay) || isdigit(overlay)) {
+
+	  for (j=0; j<SYMTAB_SIZE; j++) {
+	    if (strcasestr(alternate_symtab[j].description, description) != NULL) {
+	      *symtab = overlay;
+	      *symbol = j + ' ';
+	      return (1);
+	    }
+	  }
+	  /* If that fails should we give up or ignore the overlay and keep trying? */
+	}
+
+/*
+ * Search primary table.
+ */
+	for (j=0; j<SYMTAB_SIZE; j++) {
+	  if (strcasestr(primary_symtab[j].description, description) != NULL) {
+	    *symtab = '/';
+	    *symbol = j + ' ';
+	    return (1);
+	  }
+	}
+
+/*
+ * Search alternate table.
+ */
+	for (j=0; j<SYMTAB_SIZE; j++) {
+	  if (strcasestr(alternate_symtab[j].description, description) != NULL) {
+	    *symtab = '\\';
+	    *symbol = j + ' ';
+	    return (1);
+	  }
+	}
+
+/*
+ * Finally, the "new" symbols.
+ * Probably want this last so get get the most standard and
+ * generic for queries such as "house" or ...
+ */
+	for (j=0; j<new_sym_len; j++) {
+	  if (strcasestr(new_sym_ptr[j].description, description) != NULL) {
+	    *symtab = new_sym_ptr[j].overlay;
+	    *symbol = new_sym_ptr[j].symbol;
+	    return (1);
+	  }
+	}
+
+/*
+ * Default to something generic like house.  
+ * Caller is responsible for issuing error message.
+ */
+	*symtab = '/';
+	*symbol = '-';
+	return (0);
+
+} /* end symbols_code_from_description */
+
+
+#if 0
+
+/* Quick, incomplete, unit test. */
+/* gcc -g symbols.c textcolor.c misc.a */
+
+int main (int argc, char *argv[])
+{
+	char symtab;
+	char symbol;
+	char dest[8];
+	char description[50];
+
+	symbols_init ();
+
+
+
+	symbols_from_dest_or_src ('T', "W1ABC", "GPSC43", &symtab, &symbol);
+	if (symtab != '/' || symbol != 'K') dw_printf ("ERROR 1-1\n");
+
+	symbols_from_dest_or_src ('T', "W1ABC", "GPSE87", &symtab, &symbol);
+	if (symtab != '\\' || symbol != 'w') dw_printf ("ERROR 1-2\n");
+
+	symbols_from_dest_or_src ('T', "W1ABC", "SPCBL", &symtab, &symbol);
+	if (symtab != '/' || symbol != '+') dw_printf ("ERROR 1-3\n");
+
+	symbols_from_dest_or_src ('T', "W1ABC", "SYMST", &symtab, &symbol);
+	if (symtab != '\\' || symbol != 't') dw_printf ("ERROR 1-4\n");
+
+	symbols_from_dest_or_src ('T', "W1ABC", "GPSOD9", &symtab, &symbol);
+	if (symtab != '9' || symbol != '#') dw_printf ("ERROR 1-5\n");
+
+	symbols_from_dest_or_src ('T', "W1ABC-14", "XXXXXX", &symtab, &symbol);
+	if (symtab != '/' || symbol != 'k') dw_printf ("ERROR 1-6\n");
+
+	symbols_from_dest_or_src ('T', "W1ABC", "GPS???", &symtab, &symbol);
+	/* Outputs are left alone if symbol can't be determined. */
+	if (symtab != '/' || symbol != 'k') dw_printf ("ERROR 1-7\n");
+
+
+	symbols_into_dest ('/', 'K', dest);
+	if (strcmp(dest, "GPSC43") != 0) dw_printf ("ERROR 2-1\n");
+
+	symbols_into_dest ('\\', 'w', dest);
+	if (strcmp(dest, "GPSE87") != 0) dw_printf ("ERROR 2-2\n");
+
+	symbols_into_dest ('3', 'A', dest);
+	if (strcmp(dest, "GPSAA3") != 0) dw_printf ("ERROR 2-3\n");
+
+// Expect to see this:
+//   Could not convert symbol " A" to GPSxyz destination format.
+//   Could not convert symbol "/ " to GPSxyz destination format.
+
+	symbols_into_dest (' ', 'A', dest);
+	if (strcmp(dest, "GPS???") != 0) dw_printf ("ERROR 2-4\n");
+
+	symbols_into_dest ('/', ' ', dest);
+	if (strcmp(dest, "GPS???") != 0) dw_printf ("ERROR 2-5\n");
+
+
+
+	symbols_get_description ('J', 's', description);
+	if (strcmp(description, "Jet Ski") != 0) dw_printf ("ERROR 3-1\n");
+
+	symbols_get_description ('/', 'O', description);
+	if (strcmp(description, "BALLOON") != 0) dw_printf ("ERROR 3-2\n");
+
+	symbols_get_description ('\\', 'T', description);
+	if (strcmp(description, "Thunderstorm") != 0) dw_printf ("ERROR 3-3\n");
+
+	symbols_get_description ('5', 'T', description);
+	if (strcmp(description, "Thunderstorm w/overlay 5") != 0) dw_printf ("ERROR 3-4\n");
+
+// Expect to see this:
+//   Symbol table identifier is not '/' (primary), '\' (alternate), or valid overlay character.
+
+	symbols_get_description (' ', 'T', description);
+	if (strcmp(description, "--no-symbol--") != 0) dw_printf ("ERROR 3-5\n");
+
+	symbols_get_description ('/', ' ', description);
+	if (strcmp(description, "--no-symbol--") != 0) dw_printf ("ERROR 3-6\n");
+
+
+
+	symbols_code_from_description ('5', "girl scouts", &symtab, &symbol);
+	if (symtab != '5' || symbol != ',') dw_printf ("ERROR 4-1\n");
+
+	symbols_code_from_description (' ', "scouts", &symtab, &symbol);
+	if (symtab != '/' || symbol != ',') dw_printf ("ERROR 4-2\n");
+
+	symbols_code_from_description (' ', "girl scouts", &symtab, &symbol);
+	if (symtab != '\\' || symbol != ',') dw_printf ("ERROR 4-3\n");
+
+	symbols_code_from_description (' ', "jet ski", &symtab, &symbol);
+	if (symtab != 'J' || symbol != 's') dw_printf ("ERROR 4-4\n");
+
+	symbols_code_from_description (' ', "girl scouts", &symtab, &symbol);
+	if (symtab != '\\' || symbol != ',') dw_printf ("ERROR 4-5\n");
+
+	symbols_code_from_description (' ', "yen", &symtab, &symbol);
+	if (symtab != 'Y' || symbol != '$') dw_printf ("ERROR 4-6\n");
+
+	symbols_code_from_description (' ', "taco bell", &symtab, &symbol);
+	if (symtab != 'T' || symbol != 'R') dw_printf ("ERROR 4-7\n");
+
+
+} /* end main */
+
+#endif
+
+/* end symbols.c */
\ No newline at end of file
diff --git a/symbols.h b/symbols.h
new file mode 100755
index 0000000..cf2eca3
--- /dev/null
+++ b/symbols.h
@@ -0,0 +1,14 @@
+
+/* symbols.h */
+
+void symbols_init (void);
+
+void symbols_from_dest_or_src (char dti, char *src, char *dest, char *symtab, char *symbol);
+
+int symbols_into_dest (char symtab, char symbol, char *dest);
+
+void symbols_get_description (char symtab, char symbol, char *description);
+
+int symbols_code_from_description (char overlay, char *description, char *symtab, char *symbol);
+
+/* end symbols.h */
diff --git a/symbolsX.txt b/symbolsX.txt
new file mode 100755
index 0000000..7e91724
--- /dev/null
+++ b/symbolsX.txt
@@ -0,0 +1,364 @@
+APRS SYMBOLS (Icons)                                        07 Oct 2013
+-----------------------------------------------------------------------
+                                                                 WB4APR
+
+This original APRS symbol specification is updated periodically with 
+new symbols as they are defined.  This is THE master list for APRS. But
+almost all new symbols will be formed as Overlays to the basic set. So
+be sure to check the symbols-new.txt file noted below!
+
+NEW OVERLAYS ON ALL ALTERNATE SYMBOLS: As of 1 October 2007, the use of 
+overlay characters on all alternate symbols was allowed as needed in 
+order to further expand the APRS symbol set.  These overlay expansions 
+of up to 36 different usages for each of the 94 alternate symbols adds 
+hundreds of potential new symbols.  Since this overlay info will no 
+longer fit in this original document it is found in a new document:
+
+http://aprs.org/symbols/symbols-new.txt
+
+If an alternate symbol from the table below, contains significant
+definitions of its overlay characters, then the entry will have an "O"
+added to indicate that additonal definitions exist in the above 
+document.  The # symbol indicates the original Overlay subset.
+
+For details on Upgrading your symbol set, please see the background
+information on Symbols prepared by Stephen Smith, WA8LMF:
+http://www.ew.usna.edu/~bruninga/aprs/symbols-background.txt
+
+UPDATE CHRONOLOGY:
+
+07 Oct 13: Added JetSki [Js] & Ham Club [C-] ovrlys to symbols-new.txt
+19 Sep 11: Added T & 2 overlay for 1 & 2 hop Message TX Igates
+           Updated overlay "portable" (;) overlays for events
+20 Oct 09: Added WIRES to overlayed circles
+05 Dec 08: Clarified symbols classified as MOBILE.  
+           Added Primary: 123456789\ and ALT: Y[\
+04 Nov 08: Added notes to match file with New Overlays noted above
+           Added list of "just-mobile-symbols"
+25 Mar 08: Added \% Power plants with Overlays.  Changed
+           \i from "BOXn digi" to generic "BOX with overlay".
+           An indoor BOXn digi can use an overlay on existing digi.
+13 Feb 08: Added overlay "S" on \a and \c for SATERN
+10 Jan 08: Added Overlay "X" on BOX for the XO OLPC laptop
+01 Oct 07: Added the above paragraphs about Overlay byte extensions 
+           of the APRS symbol set and defined two new symbols.  
+           Added "S[" Skier and "O-" for House with Operator Present.
+03 Jul 07: Began defining some overlay common usages.
+09 Apr 07: First proposed expanding the alternate symbols overlays.
+           And added to the APRS1.2 addendum:
+           http://www.ew.usna.edu/~bruninga/aprs/symbols-overlays.txt
+02 Feb 07: Cleaned up some  names for consistency with Icon tables
+18 Oct 06: Suggest \x for car wreck or road obstruction. Looks 
+           like a big X like this:   ->X<-   sort-of...
+16 Jun 06: Suggest I for 2-way IGate and R overlay for RX only.
+28 Sep 05: Added /F for Farm vehicle (looks like a tractor)
+18 Jan 05: Added C overlay for CERTS to "\c" symbol
+ 3 Jan 05: Added W overlay to "\a" symbol to indicate WinLINK.
+ 7 Dec 04: the /] PBBS symbol (typically a blue Mail Box) is renamed 
+             as "MAIL/P.O.". PBBS users should use the BBS symbol.
+ 8 Sep 04: Added Military Affiliate MARS symbol as \M with overlays
+
+SYMBOLS for APRS 1.1 ADDENDUM are as below.  As of 26 July 04, the
+symbols below were approved and became part of the APRS1.1 addendum.
+
+JunJul 04 to add a Rocket "\O" and an SUV as "\k"
+06 May 04 to move Shelter(overlay) from PRI to ALT table
+ 5 Jan 04 to add Destination Waypoint "\/")
+29 Oct 03 to add 802.11, firenet, IncidentCommandPost & Shelter
+
+
+04 Feb 04:  Unassigned symbols should display the international symbol
+            of a circle with a slash through it.  Meaning "not"...
+
+29 Jan 04:  Reviewed ALL symbols in the spec.  Here are all additions:
+   \& = is not just HF, but ANY GATEWAY with overlay character            
+   /) = Wheelchair (Handicapped) useful in Marathons (blue and white)
+   \) = Firenet MEO symbol (MODIS Earth observation)
+   \/ = Waypoint (destination) a RED dot showing intended destination. 
+        Uses special processing to draw a line from a mobile to his
+        destination.  This was proposed 5 Jan 2004
+   /L = Logged-ON user.  (A PC symbol showing someone on APRS-IS)
+   /l = Laptop (lower case L) (looks like a laptop)
+   /c = Incident Command Post
+   \y = Skywarn (black tornado, orange background with white surround)
+   \z = Shelter (with overlay) (A red house with peaked roof)
+
+JUST-MOBILE-SYMBOLS:  The following two lists of symbols were defined
+as "mobile" symbols for the purposess of filtering etc.  This list
+has been published in APRS1.1 for over a decade.  As of Nov08, this
+list was reviewd and updated:
+
+WAS:
+Pri:  '<=>()*0COPRSUXY[^abefgjkpsuv
+Alt:  /0>AKOS[^knsuv
+
+IS NOW:
+Pri:  !'<=>()*0123456789CFOPRSUXY[\^abefgjkpsuv  <== [added !F\ ]
+Alt:  >KOSY[^ksuv\                               <==[removed /0An]
+
+
+
+SYMBOLS.TXT      APRS DISPLAY SYMBOLS             APRSdos    ORIGINAL
+======================================================================
+Document dated:  28 Apr 99  FInal APRSdos symbol spec (still updated!)
+Author(s):       Bob Bruninga, WB4APR <bruninga at usna.edu>
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The character after the latitude "N/S" in an APRS position report is a 
+TABLE character and the character following the longitude "E/W" is the 
+APRS symbol character.  The TABLE character selects one of two symbol 
+tables or may be used as an alphanumeric overlay over some symbols:
+
+        TABLE    RESULT            
+         &       RESERVED for possible AUXILLIARY tables (Aug 09)
+         /       Primary   symbol Table  (Mostly stations)
+         \       Alternate symbol table  (Mostly Objects)
+         0-9     Alternate OVERLAY symbol with 0-9 overlayed
+         A-Z     Alternate OVERLAY symbol with A-Z overlayed
+      
+For ease of reference we refer to these as the SYMBOL CHARACTERS and
+often abbreviate them as "/$" which refers to the Table character "/"
+and symbol character "$".
+
+Press F1-S in APRSdos to see these symbols.  Some symbols may have a
+numeric overlay character 0-9 or A-Z.  These are shown on the F1-S
+display with the numeral "3" overlayed.  The original overlayable 
+symbols through Oct 2007 were:
+
+  CIRCLE, SQUARE, CAR, TRUCK, VAN, DIGIS, GATES
+  Civil-Defense(RACES), NWS sites, WX stations, Triangle
+
+After that, provisions should be made in all software to allow for
+overlays on any alternate symbol as they may be used in the future.
+
+SYMBOLS WITH STAND-ALONE GPS TRACKERS:  Stand-alone devices that
+transmit raw GPS have no method to convey their symbol.  For this
+unique application, the symbol can be designated in the UNPROTO 
+TOADDRESS of the form GPSxyz.  The X points to a subgroup table and 
+the Y is the actual symbol.  Z is an overlay character if used.   
+Actually there are four TOCALLS that will provide the same symbol.
+
+   GPSxyz is for stand alone trackers
+   SPCxyz is for stand alone trackers at SPECIAL events in SPCL mode
+   SYMxyz is for other TNC-only stations such as WX stations
+
+Notice that both the /$ method and GPSxyz method have a one-for-one
+corrspondence for all numeric and alphabetic symbols of both upper 
+and lower case which should make them easy to remember.  For the 
+GPSxyz method, we have broken the PRIMARY and ALTERNATE tables into 
+sub tables with different names to make them easier to remember.  For 
+example, "/C" is a CANOE in the PRIMARY table which can be referred to 
+as PC in the XYZ format and the "\C" ALTERNATE table symbol is for 
+Coast Guard which could also be referred to in the GPSxyz format as 
+AC.   Simillarly, you can think  of the lower case symbols /c or \c as 
+being LC for lower case C and SC for "secondary" table "c".
+
+The Following Symbol table shows the two types of identifiers for all
+APRS ICONS.  The primary symbols are on the left and the alternate 
+table is on the right.  The first column of each is labeled /$ and \$ 
+for the primary and alternate tables.  These are the chacacters you 
+will see in an APRS formatted position report.  The XYZ columns are 
+for the stand-alone trackers described above.
+
+/$ XYZ BASIC SYMBOL TABLE        \$  XYZ OTHER SYMBOL TABLE (\)
+-- --- ------------------------  --  --- ----------------------
+/! BB  Police, Sheriff           \!  OB  EMERGENCY (!)            
+/" BC  reserved  (was rain)      \"  OC  reserved
+/# BD  DIGI (white center)       \#  OD# OVERLAY DIGI (green star)
+/$ BE  PHONE                     \$  OEO Bank or ATM  (green box) 
+/% BF  DX CLUSTER                \%  OFO Power Plant with overlay
+/& BG  HF GATEway                \&  OG# I=Igte R=RX T=1hopTX 2=2hopTX
+/' BH  Small AIRCRAFT (SSID = 7) \'  OHO Crash (& now Incident sites)
+/( BI  Mobile Satellite Station  \(  OI  CLOUDY (other clouds w ovrly)
+/) BJ  Wheelchair (handicapped)  \)  OJO Firenet MEO, MODIS Earth Obs.
+/* BK  SnowMobile                \*  OK  SNOW (& future ovrly codes)
+/+ BL  Red Cross                 \+  OL  Church
+/, BM  Boy Scouts                \,  OM  Girl Scouts
+/- BN  House QTH (VHF)           \-  ONO House (H=HF) (O = Op Present)
+/. BO  X                         \.  OO  Ambiguous (Big Question mark)
+// BP  Red Dot                   \/  OP  Waypoint Destination
+                                          See APRSdos MOBILE.txt
+
+/$ XYZ PRIMARY SYMBOL TABLE      \$  XYZ ALTERNATE SYMBOL TABLE (\)
+-- --- ------------------------  --  --- --------------------------
+/0 P0  # circle (obsolete)       \0  A0# CIRCLE (E/I/W=IRLP/Echolink/WIRES)
+/1 P1  TBD (these were numbered) \1  A1
+/2 P2  TBD (circles like pool)   \2  A2
+/3 P3  TBD (balls.  But with)    \3  A3
+/4 P4  TBD (overlays, we can)    \4  A4
+/5 P5  TBD (put all #'s on one)  \5  A5
+/6 P6  TBD (So 1-9 are available)\6  A6
+/7 P7  TBD (for new uses?)       \7  A7
+/8 P8  TBD (They are often used) \8  A8O 802.11 or other network node
+/9 P9  TBD (as mobiles at events)\9  A9  Gas Station (blue pump)  
+/: MR  FIRE                      \:  NR  Hail (& future ovrly codes)                    
+/; MS  Campground (Portable ops) \;  NSO Park/Picnic + overlay events        
+/< MT  Motorcycle     (SSID =10) \<  NTO ADVISORY (one WX flag)
+/= MU  RAILROAD ENGINE           \=  NUO APRStt Touchtone (DTMF users)
+/> MV  CAR            (SSID = 9) \>  NV# OVERLAYED CAR
+/? MW  SERVER for Files          \?  NW  INFO Kiosk  (Blue box with ?)
+/@ MX  HC FUTURE predict (dot)   \@  NX  HURICANE/Trop-Storm
+/A PA  Aid Station               \A  AA# overlayBOX DTMF & RFID & XO
+/B PB  BBS or PBBS               \B  AB  Blwng Snow (& future codes)
+/C PC  Canoe                     \C  AC  Coast Guard          
+/D PD                            \D  AD  Drizzle (proposed APRStt)
+/E PE  EYEBALL (Eye catcher!)    \E  AE  Smoke (& other vis codes)
+/F PF  Farm Vehicle (tractor)    \F  AF  Freezng rain (&future codes)
+/G PG  Grid Square (6 digit)     \G  AG  Snow Shwr (& future ovrlys)
+/H PH  HOTEL (blue bed symbol)   \H  AHO \Haze (& Overlay Hazards)
+/I PI  TcpIp on air network stn  \I  AI  Rain Shower  
+/J PJ                            \J  AJ  Lightening (& future ovrlys)
+/K PK  School                    \K  AK  Kenwood HT (W)              
+/L PL  PC user (Jan 03)          \L  AL  Lighthouse                     
+/M PM  MacAPRS                   \M  AMO MARS (A=Army,N=Navy,F=AF) 
+/N PN  NTS Station               \N  AN  Navigation Buoy          
+/O PO  BALLOON        (SSID =11) \O  AO  Rocket (new June 2004)
+/P PP  Police                    \P  AP  Parking                    
+/Q PQ  TBD                       \Q  AQ  QUAKE                       
+/R PR  REC. VEHICLE   (SSID =13) \R  ARO Restaurant                   
+/S PS  SHUTTLE                   \S  AS  Satellite/Pacsat
+/T PT  SSTV                      \T  AT  Thunderstorm        
+/U PU  BUS            (SSID = 2) \U  AU  SUNNY                       
+/V PV  ATV                       \V  AV  VORTAC Nav Aid              
+/W PW  National WX Service Site  \W  AW# # NWS site (NWS options)
+/X PX  HELO           (SSID = 6) \X  AX  Pharmacy Rx (Apothicary)
+/Y PY  YACHT (sail)   (SSID = 5) \Y  AYO Radios and devices
+/Z PZ  WinAPRS                   \Z  AZ
+/[ HS  Human/Person (HT)         \[  DSO W.Cloud (& humans w Ovrly)                 
+/\ HT  TRIANGLE(DF station)      \\  DTO New overlayable GPS symbol
+/] HU  MAIL/PostOffice(was PBBS) \]  DU
+/^ HV  LARGE AIRCRAFT            \^  DV# # Aircraft (shows heading)
+/_ HW  WEATHER Station (blue)    \_  DW# # WX site (green digi)
+/` HX  Dish Antenna              \`  DX  Rain (all types w ovrly)    
+
+/$ XYZ LOWER CASE SYMBOL TABLE   \$  XYZ SECONDARY SYMBOL TABLE (\)
+-- --- ------------------------  --  --- --------------------------
+/a LA  AMBULANCE     (SSID = 1)  \a  SA#O ARRL, ARES, WinLINK
+/b LB  BIKE          (SSID = 4)  \b  SB  Blwng Dst/Snd (& others)
+/c LC  Incident Command Post     \c  SC#O CD triangle RACES/SATERN/etc
+/d LD  Fire dept                 \d  SD  DX spot by callsign
+/e LE  HORSE (equestrian)        \e  SE  Sleet (& future ovrly codes)
+/f LF  FIRE TRUCK    (SSID = 3)  \f  SF  Funnel Cloud                
+/g LG  Glider                    \g  SG  Gale Flags                     
+/h LH  HOSPITAL                  \h  SHO Store. or HAMFST Hh=HAM store
+/i LI  IOTA (islands on the air) \i  SI# BOX or points of Interest
+/j LJ  JEEP          (SSID-12)   \j  SJ  WorkZone (Steam Shovel)
+/k LK  TRUCK         (SSID = 14) \k  SKO Special Vehicle SUV,ATV,4x4
+/l LL  Laptop (Jan 03)  (Feb 07) \l  SL  Areas      (box,circles,etc)
+/m LM  Mic-E Repeater            \m  SM  Value Sign (3 digit display)   
+/n LN  Node (black bulls-eye)    \n  SN# OVERLAY TRIANGLE         
+/o LO  EOC                       \o  SO  small circle                    
+/p LP  ROVER (puppy, or dog)     \p  SP  Prtly Cldy (& future ovrlys)            
+/q LQ  GRID SQ shown above 128 m \q  SQ
+/r LR  Repeater         (Feb 07) \r  SR  Restrooms                
+/s LS  SHIP (pwr boat)  (SSID-8) \s  SS# OVERLAY SHIP/boat (top view)
+/t LT  TRUCK STOP                \t  ST  Tornado                  
+/u LU  TRUCK (18 wheeler)        \u  SU# OVERLAYED TRUCK
+/v LV  VAN           (SSID = 15) \v  SV# OVERLAYED Van
+/w LW  WATER station             \w  SW  Flooding                 
+/x LX  xAPRS (Unix)              \x  SX  Wreck or Obstruction ->X<-                                
+/y LY  YAGI @ QTH                \y  SY  Skywarn
+/z LZ  TBD                       \z  SZ# OVERLAYED Shelter 
+/{ J1                            \{  Q1  Fog (& future ovrly codes)
+/| J2  TNC Stream Switch         \|  Q2  TNC Stream Switch
+/} J3                            \}  Q3
+/~ J4  TNC Stream Switch         \~  Q4  TNC Stream Switch
+
+HEADING SYMBOLS:  Although all symbols are supposed to have a heading
+line showing the direction of movement with a length proportional to
+the log of speed, some symbols were desiged as top-down views so that 
+they could be displayed actually always POINTING in the direction of 
+movement.  These special symbols are:
+ 
+\> OVERLAYED CAR
+\s Overlayed Ship
+\^ Overlayed Aircraft
+/^ Aircraft
+/g Glider
+\n Overlayed Triangle
+
+AREA SYMBOLS!  You can define BOX/CIRCLE/LINE or TRIANGLE areas in all
+colors, either open or filled in, any size from 60 feet to 100 miles. 
+Simply move the cursor to the location, press HOME, move the cursor to 
+the lower right corner of the AREA and hit INPUT-ADD-OBJECTS-AREA.  
+Enter the type of area, and color.  NOTE that AREA shapes can only be 
+defined by selecting the upper left corner first, then the lower right 
+second.  The line is an exception.  It is still top to bottom, but the 
+lower point can be to the left of the beginning point.  Further, in 
+the line option you may specify a "width" either side of the central 
+line.
+
+These AREAS are useful for real-time events such as for a search-and-
+rescue, or adding a special ROAD or ROUTE for a special event.  Be 
+cautious in using the color fill option, since all other objects in 
+that area that occur earlier in your PLIST will be obscured.  AND you 
+do NOT know the order of other stations P-lists. 
+
+AREAS FORMAT: The new format for specifying special areas uses the 
+CSE/SPD field to provide the additional information as follows:
+
+$CSE/SPD...  Normal Field description
+lTyy/Cxx...  Where:  l (lower case L) is  symbol for "LOCATION SHAPES"
+                     T is Type of shape:  0=circle, 1=line, 2=elipse
+                                          3=triangle 4=box
+                                          add 5 to these => color-in
+                     C is color from 0 to 15.  For colors geater than
+                       9, / is replaced with a 1.
+                    yy is sqroot of the latitude offset in 1/100ths
+                    xx is sqroot of the longitude offset
+                       
+These offsets are ALWAYS positive to the right and down, except for 
+the special case of a lower right quadrant line, these are given the 
+Type of 6 and are drawn down and to the left.
+
+HURRICANES, TROPICAL STORMS and DEPRESSIONS:  These symbols will be
+differentiated by colors red, yellos, and blue.  Additionally a radius
+of Huricane and also Tropical storm winds will also be shown if the
+format detailed in WX.txt is used.
+
+SYMBOLS ON MAPS!  APRS can also be permanently embedded in maps.  To 
+embed a symbol in a map, simply make the first four characters of the 
+label be a # followed by the dual symbol character, followed by a hex 
+number from 1 to F that indicates the color for the symbol.  The 
+remaining 8 characters can be used for a conventional label at the 
+same location.  An example are the VORTAC nav-aids in Alaska.  The 
+Anchorage VORTAC appears as ANC on all maps below 128 miles. The label 
+entry is #\VFANC,LAT,LONG,128.
+
+VALUE SIGNPOSTS:  Signposts display as a yellow box with a 1-3 letter 
+overlay on them.  You specify the 1-3 letter overlay by enclosing them 
+in braces in the comment field.  Thus a VALUE Signpost with {55} would 
+appear as a sign with 55 on it, designed for posting the speed 
+of traffic past speed measuring devices. APRSdos has a version named
+APRStfc.EXE that monitors traffic speed and posts these speed signs
+objects when traffic slows in these areas.  To avoid cluttering the 
+map, they ONLY appear at 8 miles and below AND they do not show any 
+callsign or name.  Only the yellow box and the 3 letters or numbers.  
+Select them from the OBJECT menu under VALUE...
+
+APRS 1.2 OVERLAY TYPE SYMBOLS [April 2007]:
+-------------------------------------------
+
+All alternate symbols have the potential to be overlayed.  This was 
+the original intent and was only limited to a few due to limitations 
+in Mac and WinAPRS.  Those original "numbered" symbols are marked 
+with a # in the table above.  But by 2007, it was time to move on.  
+In APRS 1.2 it is proposed that any ALTENATE symbol can have overlays.
+Kenwood has already responded with the new D710 that can now display 
+these overlays on all symbols.  
+
+To help define these hundreds of new symbol combinations, we have
+added a new file called:
+
+http://www.ew.usna.edu/~bruninga/aprs/symbols-new.txt
+
+The overlay symbols may be used in two ways.  First, simply as an 
+overlay on a basic symbol type.  Most uses of these symbols will be
+in this manner.  But special overlays on some special characters
+may also be re-drawn as entirely new graphics for clarity if desired.
+
+The above NEW-Overlay document attempts to define those special
+combinations that may rate their own special symbol or where multiple
+use of an overlay character for different purposes would be confusing.
+
+Bob, WB4APR
diff --git a/textcolor.c b/textcolor.c
new file mode 100755
index 0000000..8d3cf52
--- /dev/null
+++ b/textcolor.c
@@ -0,0 +1,345 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        textcolor.c
+ *
+ * Purpose:     Originally this would only set color of text
+ *	 	and we used printf everywhere.
+ *		Now we also have a printf replacement that can
+ *		be used to redirect all output to the desired place.
+ *		This opens the door to using ncurses, a GUI, or
+ *		running as a daemon.
+ *
+ * Description:	For Linux and Cygwin use the ANSI escape sequences.
+ *		In earlier versions of Windows, the cmd window and ANSI.SYS
+ *		could interpret this but it doesn't seem to be available
+ *		anymore so we use a different interface.
+ *
+ * References:
+ *		http://en.wikipedia.org/wiki/ANSI_escape_code
+ *		http://academic.evergreen.edu/projects/biophysics/technotes/program/ansi_esc.htm
+ *
+ * Problem:	The ANSI escape sequences, used on Linux, allow 8 basic colors.
+ *		Unfortunately, white is not one of them.  We only have dark
+ *		white, also known as light gray.  To get brighter colors, 
+ *		we need to apply an attribute.  On some systems, the bold 
+ *		attribute produces a brighter color rather than a bold font.
+ *		On other systems, we need to use the blink attribute to get 
+ *		bright colors, including white.  However on others, blink
+ *		does actually produce blinking characters.
+ *
+ *		Several people have also complained that bright green is
+ *		very hard to read against a light background.  The current
+ *		implementation does not allow easy user customization of colors.
+ *		
+ *		Currently, the only option is to put "-t 0" on the command
+ *		line to disable all text color.  This is more readable but
+ *		makes it harder to distinguish different types of
+ *		information, e.g. received packets vs. error messages.
+ *
+ *		A few people have suggested ncurses.  This needs to 
+ *		be investigated for a future version.   The foundation has
+ *		already been put in place.  All of the printf's have been
+ *		replaced by dw_printf, defined in this file.   All of the
+ *		text output is now being funneled thru this one function
+ *		so it should be easy to send it to the user by some
+ *		other means.
+ *
+ *--------------------------------------------------------------------*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+
+#if __WIN32__
+
+// /* Missing from MinGW header file. */
+// #define vsprintf_s vsnprintf
+
+//_CRTIMP int __cdecl __MINGW_NOTHROW vsprintf_s (char*, size_t, const char*, __VALIST);
+
+//int vsprintf_s(
+//   char *buffer,
+//   size_t numberOfElements,
+//   const char *format,
+//   va_list argptr 
+//); 
+
+
+#include <windows.h>
+
+#define BACKGROUND_WHITE (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY)
+
+
+
+#elif __CYGWIN__	/* Cygwin */
+
+/* For Cygwin we need "blink" (5) rather than the */
+/* expected bright/bold (1) to get bright white background. */
+/* Makes no sense but I stumbled across that somewhere. */
+
+static const char background_white[] = "\e[5;47m";
+
+/* Whenever a dark color is used, the */
+/* background is reset and needs to be set again. */
+
+static const char black[]	= "\e[0;30m" "\e[5;47m";
+static const char red[] 	= "\e[1;31m";
+static const char green[] 	= "\e[1;32m";
+static const char yellow[] 	= "\e[1;33m";
+static const char blue[] 	= "\e[1;34m";
+static const char magenta[] 	= "\e[1;35m";
+static const char cyan[] 	= "\e[1;36m";
+static const char dark_green[]	= "\e[0;32m" "\e[5;47m";
+
+/* Clear from cursor to end of screen. */
+
+static const char clear_eos[]	= "\e[0J";
+
+
+#else 	/* Linux */
+
+static const char background_white[] = "\e[47;1m";
+
+/* Whenever a dark color is used, the */
+/* background is reset and needs to be set again. */
+
+static const char black[]	= "\e[0;30m" "\e[1;47m";
+static const char red[] 	= "\e[1;31m" "\e[1;47m";
+static const char green[] 	= "\e[1;32m" "\e[1;47m"; 
+static const char yellow[] 	= "\e[1;33m" "\e[1;47m";
+static const char blue[] 	= "\e[1;34m" "\e[1;47m";
+static const char magenta[] 	= "\e[1;35m" "\e[1;47m";
+static const char cyan[] 	= "\e[1;36m" "\e[1;47m";
+static const char dark_green[]	= "\e[0;32m" "\e[1;47m";
+
+/* Clear from cursor to end of screen. */
+
+static const char clear_eos[]	= "\e[0J";
+
+#endif	/* end Linux */
+
+
+#include "textcolor.h"
+
+
+/*
+ * g_enable_color:
+ *	0 = disable text colors.
+ *	1 = normal.
+ *	others... future possibility.
+ */
+
+static int g_enable_color = 1;
+
+
+void text_color_init (int enable_color)
+{
+
+	g_enable_color = enable_color;
+
+
+#if __WIN32__
+
+
+	if (g_enable_color) {
+
+	  HANDLE h;
+	  CONSOLE_SCREEN_BUFFER_INFO csbi;
+	  WORD attr = BACKGROUND_WHITE;
+	  DWORD length;
+	  COORD coord;
+	  DWORD nwritten;
+
+	  h = GetStdHandle(STD_OUTPUT_HANDLE);
+	  if (h != NULL && h != INVALID_HANDLE_VALUE) {
+
+	    GetConsoleScreenBufferInfo (h, &csbi);
+
+	    length = csbi.dwSize.X * csbi.dwSize.Y;
+	    coord.X = 0; 
+	    coord.Y = 0;
+	    FillConsoleOutputAttribute (h, attr, length, coord, &nwritten);
+	  }
+	}
+
+#else
+	if (g_enable_color) {
+	  //printf ("%s", clear_eos);
+	  printf ("%s", background_white);
+	  //printf ("%s", clear_eos);
+	  printf ("%s", black);
+	}
+#endif
+}
+
+
+#if __WIN32__
+
+/* Seems that ANSI.SYS is no longer available. */
+
+
+void text_color_set ( enum dw_color_e c )
+{
+	WORD attr;
+	HANDLE h;
+
+	if (g_enable_color == 0) {
+	  return;
+	}
+
+	switch (c) {
+
+	  default:
+	  case DW_COLOR_INFO:
+	    attr = BACKGROUND_WHITE;
+	    break;
+
+	  case DW_COLOR_ERROR:
+	    attr = FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_WHITE;
+	    break;
+
+	  case DW_COLOR_REC:
+	    attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_WHITE;
+	    break;
+
+	  case DW_COLOR_DECODED:
+	    attr = FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_WHITE;
+	    break;
+
+	  case DW_COLOR_XMIT:
+	    attr = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_WHITE;
+	    break;
+
+	  case DW_COLOR_DEBUG:
+	    attr = FOREGROUND_GREEN | BACKGROUND_WHITE;
+	    break;
+	}
+
+	h = GetStdHandle(STD_OUTPUT_HANDLE);
+
+	if (h != NULL && h != INVALID_HANDLE_VALUE) {
+	  SetConsoleTextAttribute (h, attr);
+	}
+}
+
+#else
+
+void text_color_set ( enum dw_color_e c )
+{
+
+	if (g_enable_color == 0) {
+	  return;
+	}
+
+	switch (c) {
+
+	  default:
+	  case DW_COLOR_INFO:
+	    printf ("%s", black);
+	    break;
+
+	  case DW_COLOR_ERROR:
+	    printf ("%s", red);
+	    break;
+
+	  case DW_COLOR_REC:
+	    printf ("%s", green);
+	    break;
+
+	  case DW_COLOR_DECODED:
+	    printf ("%s", blue);
+	    break;
+
+	  case DW_COLOR_XMIT:
+	    printf ("%s", magenta);
+	    break;
+
+	  case DW_COLOR_DEBUG:
+	    printf ("%s", dark_green);
+	    break;
+	}
+}
+
+#endif
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        dw_printf 
+ *
+ * Purpose:     printf replacement that allows us to send all text
+ *		output to stdout or other desired destination.
+ *
+ * Inputs:	fmt	- C language format.
+ *		...	- Addtional arguments, just like printf.
+ *
+ *
+ * Returns:	Number of characters in result.
+ *
+ * Bug:		Fixed size buffer.  
+ *		I'd rather not do a malloc for each print.
+ *
+ *--------------------------------------------------------------------*/
+
+
+// TODO: replace all printf, look for stderr, perror
+// TODO:   $ grep printf *.c | grep -v dw_printf | grep -v fprintf | gawk '{ print $1 }' |  sort -u
+
+
+int dw_printf (const char *fmt, ...) 
+{
+#define BSIZE 1000
+	va_list args;
+	char buffer[BSIZE];
+	int len;
+	
+	va_start (args, fmt);
+	len = vsnprintf (buffer, BSIZE, fmt, args);
+	va_end (args);
+
+// TODO: other possible destinations...
+
+	fputs (buffer, stdout);
+	return (len);
+}
+
+
+
+#if TESTC
+main () 
+{
+	printf ("Initial condition\n");
+	text_color_init (1);
+	printf ("After text_color_init\n");
+	text_color_set(DW_COLOR_INFO); 		printf ("Info\n");
+	text_color_set(DW_COLOR_ERROR); 	printf ("Error\n");
+	text_color_set(DW_COLOR_REC); 		printf ("Rec\n");
+	text_color_set(DW_COLOR_DECODED); 	printf ("Decoded\n");
+	text_color_set(DW_COLOR_XMIT); 		printf ("Xmit\n");
+	text_color_set(DW_COLOR_DEBUG); 	printf ("Debug\n");
+}
+#endif
+	
+/* end textcolor.c */
diff --git a/textcolor.h b/textcolor.h
new file mode 100755
index 0000000..3d07e0b
--- /dev/null
+++ b/textcolor.h
@@ -0,0 +1,53 @@
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        textcolor.h
+ *
+ * Purpose:     Set color of text.
+ *
+ *--------------------------------------------------------------------*/
+
+enum dw_color_e { 	DW_COLOR_INFO,		/* black */
+			DW_COLOR_ERROR,		/* red */
+			DW_COLOR_REC,		/* green */
+			DW_COLOR_DECODED,	/* blue */
+			DW_COLOR_XMIT,		/* magenta */
+			DW_COLOR_DEBUG		/* dark_green */
+		};
+
+typedef enum dw_color_e dw_color_t;
+
+			
+void text_color_init (int enable_color);
+void text_color_set (dw_color_t c);
+void text_color_term (void);
+
+
+/* Degree symbol. */
+
+#if __WIN32__
+
+//#define CH_DEGREE "\xc2\xb0"	/* UTF-8. */
+
+#define CH_DEGREE " "
+
+
+#else
+
+/* Maybe we could change this based on LANG environment variable. */
+
+//#define CH_DEGREE "\xc2\xb0"	/* UTF-8. */
+
+#define CH_DEGREE " "
+
+#endif
+
+
+
+int dw_printf (const char *fmt, ...) 
+#if __WIN32__
+				__attribute__((format(ms_printf,1,2)));		/* Win C lib. */
+#else
+				__attribute__((format(printf,1,2)));		/* gnu C lib. */
+#endif
+
diff --git a/tocalls.txt b/tocalls.txt
new file mode 100755
index 0000000..a44bf2b
--- /dev/null
+++ b/tocalls.txt
@@ -0,0 +1,209 @@
+APRS TO-CALL VERSION NUMBERS                            18 Dec 2013
+-------------------------------------------------------------------
+                                                             WB4APR
+18 Dec 13 added APZWKR for GM1WKR NetSked application
+22 Oct 13 added APFIxx for APRS.FI OH7LZB, Hessu
+23 Aug 13 added APOxxx for OSCAR satellites for AMSAT-LU by LU9DO
+22 Feb 13 added APNWxx SQ3FYK.com & SQ3PLX http://microsat.com.pl/
+            and APMIxx SQ3PLX http://microsat.com.pl/
+29 Jan 13 added APICxx for HA9MCQ Pic IGate
+23 Jan 13 added APWAxx APRSISCE Android version
+18 Jan 13 added APDGxx,APDHxx,APDOxx,APDDxx,APDKxx,APD4xx for Dstar
+13 Jan 13 added APLMxx WA0TQG transceiver controller
+17 Dec 12 added APAMxx Altus Metrum GPS trackers
+03 Dec 12 added APUDRx NW Digital Radio's UDR (APRS/Dstar)
+03 Nov 12 added APHAXn SM2APRS by PY2UEP
+17 Sep 12 added APSCxx aprsc APRS-IS core server (OH7LZB, OH2MQK)
+12 Sep 12 added APSARx for ZL4FOX's SARTRACK
+02 Jul 12 added APDGxx D-Star Gateways by G4KLX
+28 Jun 12 added APDInn DIXPRS - Bela, HA5DI
+27 jun 12 added APMGxx MiniGate - Alex, AB0TJ
+17 Feb 12 added APJYnn KA2DDO yet another APRS system
+20 Jan 12 added APDSXX SP9UOB for dsDigi and ds-tracker
+                       APBPQx  John G8BPQ Digipeater/IGate
+                       APLQRU Charlie - QRU Server
+11 Jan 12 added APYTxx for YagTracker and updated Yaesu APY008/350
+
+In APRS, the AX.25  Destination address is not used for packet 
+routing as is normally done in AX.25.  So APRS uses it for two 
+things.  The initial APxxxx is used as a group identifier to make 
+APRS packets instanantly recognizable on shared channels.  Most 
+applicaitons ignore all non APRS packets.  The remaining 4 xxxx
+bytes of the field are available to indicate the software version 
+number or application.  The following applications have requested 
+a TOCALL number series:
+
+ APn  3rd digit is a number
+      AP1WWX  TAPR T-238+ WX station
+      AP4Rxy  APRS4R software interface
+      APnnnD  Painter Engineering uSmartDigi D-Gate DSTAR Gateway
+      APnnnU  Painter Engineering uSmartDigi Digipeater
+ APA  APAFxx  AFilter.
+      APAGxx  AGATE
+      APAGWx  SV2AGW's AGWtracker
+      APALxx  Alinco DR-620/635 internal TNC digis. "Hachi" ,JF1AJE
+      APAXxx  AFilterX.
+      APAHxx  AHub
+      APAND1  APRSdroid (replaced by APDRxx
+      APAMxx  Altus Metrum GPS trackers
+      APAWxx  AGWPE 
+ APB  APBxxx  Beacons or Rabbit TCPIP micros?
+      APBLxx  BigRedBee BeeLine  
+      APBLO   MOdel Rocketry K7RKT
+      APBPQx  John G8BPQ Digipeater/IGate 
+ APC  APCxxx  Cellular applications
+      APCBBx  for VE7UDP Blackberry Applications
+      APCLEY  EYTraker GPRS/GSM tracker by ZS6EY
+      APCLWX  EYWeather GPRS/GSM WX station by ZS6EY
+      APCLEZ  Telit EZ10 GSM application ZS6CEY
+      APCYxx  Cybiko applications
+ APD  APD4xx  UP4DAR platform
+      APDDxx  DV-RPTR Modem and Control Center Software 
+      APDFxx  Automatic DF units
+      APDGxx  D-Star Gateways by G4KLX ircDDB
+      APDHxx  WinDV (DUTCH*Star DV Node for Windows)
+      APDInn  DIXPRS - Bela, HA5DI
+      APDKxx  KI4LKF g2_ircddb Dstar gateway software
+      APDOxx  ON8JL Standalone DStar Node
+      APDPRS  D-Star originated posits
+      APDRxx  APrsDRoid replaces old APAND1.
+      APDSXX  SP9UOB for dsDigi and ds-tracker
+      APDTxx  APRStouch Tone (DTMF)
+      APDUxx  U2APRS by JA7UDE
+      APDWxx  DireWolf, WB2OSZ
+ APE  APExxx  Telemetry devices
+      APERXQ  Experimental tracker by PE1RXQ
+ APF  APFxxx  Firenet
+      APFGxx  Flood Gage (KP4DJT)
+      APFIxx  for APRS.FI OH7LZB, Hessu
+ APG  APGxxx  Gates, etc
+      APGOxx  for AA3NJ PDA application
+ APH  APHKxx  for LA1BR tracker/digipeater
+      APHAXn  SM2APRS by PY2UEP
+ API  APICQx  for ICQ
+      APICxx  for HA9MCQ Pic IGate
+ APJ  APJAxx  JavAPRS
+      APJExx  JeAPRS
+      APJIxx  jAPRSIgate
+      APJSxx  javAPRSSrvr
+      APJYnn  KA2DDO Yet another APRS system
+ APK  APK0xx  Kenwood TH-D7's
+      APK003  Kenwood TH-D72
+      APK1xx  Kenwood D700's
+      APK102  Kenwood D710
+      APKRAM  KRAMstuff.com - Mark. G7LEU
+ APL  APLQRU  Charlie - QRU Server
+      APLMxx  WA0TQG transceiver controller
+ APM  APMxxx  MacAPRS, 
+      APMGxx  MiniGate - Alex, AB0TJ
+      APMIxx  SQ3PLX http://microsat.com.pl/
+ APN  APNxxx  Network nodes, digis, etc
+      APN3xx  Kantronics KPC-3 rom versions
+      APN9xx  Kantronics KPC-9612 Roms
+      APNAxx  WB6ZSU's APRServe
+      APNDxx  DIGI_NED
+      APNK01  Kenwood D700 (APK101) type
+      APNK80  KAM version 8.0
+      APNKMP  KAM+
+      APNMxx  MJF TNC roms
+      APNPxx  Paccom TNC roms
+      APNTxx  SV2AGW's TNT tnc as a digi
+      APNUxx  UIdigi
+      APNXxx  TNC-X  (K6DBG)
+      APNWxx  SQ3FYK.com WX/Digi and SQ3PLX http://microsat.com.pl/
+ APO  APRSpoint
+      APOLUx  for OSCAR satellites for AMSAT-LU by LU9DO
+      APOAxx  OpenAPRS - Greg Carter
+      APOTxx  Open Track
+      APOD1w  Open Track with 1 wire WX
+      APOU2k  Open Track for Ultimeter
+      APOZxx  www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO
+ APP  APPTxx  KetaiTracker by  JF6LZE, Takeki (msg capable)
+ APQ  APQxxx  Earthquake data
+ APR  APR8xx  APRSdos versions 800+
+      APRDxx  APRSdata, APRSdr
+      APRGxx  aprsg igate software, OH2GVE
+      APRHH2  HamHud 2
+      APRKxx  APRStk
+      APRNOW  W5GGW ipad application
+      APRRTx  RPC electronics
+      APRS    Generic, (obsolete. Digis should use APNxxx instead)
+      APRXxx  >40 APRSmax
+      APRXxx  <39 for OH2MQK's RX-igate
+      APRTLM  used in MIM's and Mic-lites, etc
+      APRtfc  APRStraffic
+      APRSTx  APRStt (Touch tone)
+ APS  APRS+SA, etc
+      APSARx  ZL4FOX's SARTRACK
+      APSCxx  aprsc APRS-IS core server (OH7LZB, OH2MQK)
+      APSK63  APRS Messenger -over-PSK63
+      APSK25  APRS Messenger GMSK-250
+ APT  APTIGR  TigerTrack
+      APTTxx  Tiny Track
+      APT2xx  Tiny Track II
+      APT3xx  Tiny Track III
+      APTAxx  K4ATM's tiny track
+      APTWxx  Byons WXTrac
+      APTVxx  for ATV/APRN and SSTV applications
+ APU  APU1xx  UIview 16 bit applications
+      APU2xx  UIview 32 bit apps
+      APU3xx  UIview terminal program
+      APUDRx  NW Digital Radio's UDR (APRS/Dstar)
+ APV  APVxxx  Voice over Internet  applications
+      APVRxx  for IRLP
+      APVLxx  for I-LINK
+      APVExx  for ECHO link
+ APW  APWxxx  WinAPRS, etc
+      APWAxx  APRSISCE Android version
+      APWSxx  DF4IAN's WS2300 WX station
+      APWMxx  APRSISCE KJ4ERJ
+      APWWxx  APRSISCE win32 version
+ APX  APXnnn  Xastir
+      APXRnn  Xrouter
+ APY  APYxxx  Yeasu
+      APY008  Yaesu VX-8 series
+      APY350  Yaesu FTM-350 series
+      APYTxx  for YagTracker
+ APZ  APZxxx  Experimental
+      APZ0xx  Xastir (old versions. See APX)
+      APZMDR  for HaMDR trackers - hessu * hes.iki.fi]
+      APZPAD  Smart Palm 
+      APZTKP  TrackPoint, Nick N0LP (Balloon tracking)
+      APZWIT  MAP27 radio (Mountain Rescue) EI7IG
+      APZWKR  GM1WKR NetSked application
+
+Authors with similar alphabetic requirements are encouraged to share
+their address space with other software.  Work out agreements amongst
+yourselves and keep me informed.
+
+
+REGISTERED ALTNETS:
+-------------------
+
+ALTNETS are uses of the AX-25 tocall to distinguish specialized
+traffic that may be flowing on the APRS-IS, but that are not intended
+to be part of normal APRS distribution to all normal APRS software
+operating in normal (default) modes.  Proper APRS software that
+honors this design are supposed to IGNORE all ALTNETS unless the 
+particular operator has selected an ALTNET to monitor for.    
+
+An example is when testing; an author may want to transmit objects 
+all over his map for on-air testing, but does not want these to
+clutter everyone's maps or databases.  He could use the ALTNET of
+"TEST" and client APRS software that respects the ALTNET concept
+should ignore these packets.
+
+An ALTNET is defined to be ANY AX.25 TOCALL that is NOT one of the
+normal APRS TOCALL's.  The normal TOCALL's that APRS is supposed to
+process are:  ALL, BEACON, CQ, QST, GPSxxx and of course APxxxx.
+
+The following is a list of ALTNETS that may be of interest to other
+users.  This list is by no means complete, since ANY combination of
+characters other than APxxxx are considered an ALTNET.  But this list 
+can give consisntecy to ALTNETS that may be using the global APRS-IS 
+and need some special recognition:
+
+  TEST   - A generic ALTNET for use during testing
+  PSKAPR - PSKmail .  But it is not AX.25 anyway
+
+de WB4APR, Bob
diff --git a/tq.c b/tq.c
new file mode 100755
index 0000000..5b4c1d9
--- /dev/null
+++ b/tq.c
@@ -0,0 +1,599 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      tq.c
+ *
+ * Purpose:   	Transmit queue - hold packets for transmission until the channel is clear.
+ *		
+ * Description:	Producers of packets to be transmitted call tq_append and then
+ *		go merrily on their way, unconcerned about when the packet might
+ *		actually get transmitted.
+ *
+ *		Another thread waits until the channel is clear and then removes
+ *		packets from the queue and transmits them.
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "audio.h"
+#include "tq.h"
+#include "dedupe.h"
+
+
+
+static int tq_num_channels;			/* Set once during intialization and */
+						/* should not change after that. */
+
+static packet_t queue_head[MAX_CHANS][TQ_NUM_PRIO];	/* Head of linked list for each queue. */
+
+#if __WIN32__
+
+static CRITICAL_SECTION tq_cs;			/* Critical section for updating queues. */
+
+static HANDLE wake_up_event;			/* Notify transmit thread when queue not empty. */
+
+#else
+
+static pthread_mutex_t tq_mutex;		/* Critical section for updating queues. */
+
+static pthread_cond_t wake_up_cond;			/* Notify transmit thread when queue not empty. */
+
+static pthread_mutex_t wake_up_mutex;		/* Required by cond_wait. */
+
+static int xmit_thread_is_waiting = 0;
+
+#endif
+
+static int tq_is_empty (void);
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        tq_init
+ *
+ * Purpose:     Initialize the transmit queue.
+ *
+ * Inputs:	nchan		- Number of communication channels.
+ *
+ * Outputs:	
+ *
+ * Description:	Initialize the queue to be empty and set up other
+ *		mechanisms for sharing it between different threads.
+ *
+ *		We have different timing rules for different types of
+ *		packets so they are put into different queues.
+ *
+ *		High Priority -
+ *
+ *			Packets which are being digipeated go out first.
+ *			Latest recommendations are to retransmit these
+ *			immdediately (after no one else is heard, of course)
+ *			rather than waiting random times to avoid collisions.
+ *			The KPC-3 configuration option for this is "UIDWAIT OFF".
+ *
+ *		Low Priority - 
+ *
+ *			Other packets are sent after a random wait time
+ *			(determined by PERSIST & SLOTTIME) to help avoid
+ *			collisions.	
+ *
+ *		If more than one audio channel is being used, a separate
+ *		pair of transmit queues is used for each channel.
+ *	
+ *--------------------------------------------------------------------*/
+
+
+void tq_init (int nchan)
+{
+	int c, p;
+	int err;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_init ( %d )\n", nchan);
+#endif
+	tq_num_channels = nchan;
+	assert (tq_num_channels >= 1 && tq_num_channels <= MAX_CHANS);
+
+	for (c=0; c<MAX_CHANS; c++) {
+	  for (p=0; p<TQ_NUM_PRIO; p++) {
+	    queue_head[c][p] = NULL;
+	  }
+	}
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_init: pthread_mutex_init...\n");
+#endif
+
+#if __WIN32__
+	InitializeCriticalSection (&tq_cs);
+#else
+	err = pthread_mutex_init (&wake_up_mutex, NULL);
+	err = pthread_mutex_init (&tq_mutex, NULL);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("tq_init: pthread_mutex_init err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_init: pthread_cond_init...\n");
+#endif
+
+#if __WIN32__
+
+	wake_up_event = CreateEvent (NULL, 0, 0, NULL);
+
+	if (wake_up_event == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("tq_init: pthread_cond_init: can't create transmit wake up event");
+	  exit (1);
+	}
+
+#else
+	err = pthread_cond_init (&wake_up_cond, NULL);
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_init: pthread_cond_init returns %d\n", err);
+#endif
+
+
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("tq_init: pthread_cond_init err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+
+	xmit_thread_is_waiting = 0;
+#endif
+
+
+} /* end tq_init */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        tq_append
+ *
+ * Purpose:     Add a packet to the end of the specified transmit queue.
+ *
+ * Inputs:	chan	- Channel, 0 is first.
+ *
+ *		prio	- Priority, use TQ_PRIO_0_HI or TQ_PRIO_1_LO.
+ *
+ *		pp	- Address of packet object.
+ *				Caller should NOT make any references to
+ *				it after this point because it could
+ *				be deleted at any time.
+ *
+ * Outputs:	
+ *
+ * Description:	Add packet to end of linked list.
+ *		Signal the transmit thread if the queue was formerly empty.
+ *
+ * IMPORTANT!	Don't make an further references to the packet object after
+ *		giving it to tq_append.
+ *
+ *--------------------------------------------------------------------*/
+
+void tq_append (int chan, int prio, packet_t pp)
+{
+//	int was_empty;
+//	int c, p;
+	packet_t plast;
+	packet_t pnext;
+	int err;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_append (chan=%d, prio=%d, pp=%p)\n", chan, prio, pp);
+#endif
+	assert (tq_num_channels >= 1 && tq_num_channels <= MAX_CHANS);
+	assert (prio >= 0 && prio < TQ_NUM_PRIO);
+
+	if (chan < 0 || chan >= tq_num_channels) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("ERROR - Request to transmit on radio channel %d.\n", chan);
+	  ax25_delete(pp);
+	  return;
+	}
+
+/* Is transmit queue out of control? */
+
+	if (tq_count(chan,prio) > 20) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Transmit packet queue is too long.  Discarding transmit request.\n");
+	  dw_printf ("Perhaps the channel is so busy there is no opportunity to send.\n");
+	  ax25_delete(pp);
+	  return;
+	}
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_append: enter critical section\n");
+#endif
+#if __WIN32__
+	EnterCriticalSection (&tq_cs);
+#else
+	err = pthread_mutex_lock (&tq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("tq_append: pthread_mutex_lock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+//	was_empty = 1;
+//	for (c=0; c<tq_num_channels; c++) {
+//	  for (p=0; p<TQ_NUM_PRIO; p++) {
+//	    if (queue_head[c][p] != NULL)
+//	       was_empty = 0;
+//	  }
+//	}
+
+	if (queue_head[chan][prio] == NULL) {
+	  queue_head[chan][prio] = pp;
+	}
+	else {
+	  plast = queue_head[chan][prio];
+	  while ((pnext = ax25_get_nextp(plast)) != NULL) {
+	    plast = pnext;
+	  }
+	  ax25_set_nextp (plast, pp);
+	}
+
+
+#if __WIN32__ 
+	LeaveCriticalSection (&tq_cs);
+#else
+	err = pthread_mutex_unlock (&tq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("tq_append: pthread_mutex_unlock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_append: left critical section\n");
+	dw_printf ("tq_append (): about to wake up xmit thread.\n");
+#endif
+
+#if __WIN32__
+	SetEvent (wake_up_event);
+#else
+	if (xmit_thread_is_waiting) {
+
+	  err = pthread_mutex_lock (&wake_up_mutex);
+	  if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("tq_append: pthread_mutex_lock wu err=%d", err);
+	    perror ("");
+	    exit (1);
+	  }
+
+	  err = pthread_cond_signal (&wake_up_cond);
+	  if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("tq_append: pthread_cond_signal err=%d", err);
+	    perror ("");
+	    exit (1);
+	  }
+
+	  err = pthread_mutex_unlock (&wake_up_mutex);
+	  if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("tq_append: pthread_mutex_unlock wu err=%d", err);
+	    perror ("");
+	    exit (1);
+	  }
+	}
+#endif
+
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        tq_wait_while_empty
+ *
+ * Purpose:     Sleep while the transmit queue is empty rather than
+ *		polling periodically.
+ *
+ * Inputs:	None.
+ *		
+ *--------------------------------------------------------------------*/
+
+
+void tq_wait_while_empty (void)
+{
+	int is_empty;
+	//int c, p;
+	int err;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_wait_while_empty () : enter critical section\n");
+#endif
+	assert (tq_num_channels >= 1 && tq_num_channels <= MAX_CHANS);
+
+
+#if __WIN32__
+	EnterCriticalSection (&tq_cs);
+#else
+	err = pthread_mutex_lock (&tq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("tq_wait_while_empty: pthread_mutex_lock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+#if DEBUG
+	//text_color_set(DW_COLOR_DEBUG);
+	//dw_printf ("tq_wait_while_empty (): after pthread_mutex_lock\n");
+#endif
+	is_empty = tq_is_empty();
+
+#if __WIN32__
+	LeaveCriticalSection (&tq_cs);
+#else
+	err = pthread_mutex_unlock (&tq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("tq_wait_while_empty: pthread_mutex_unlock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_wait_while_empty () : left critical section\n");
+#endif
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_wait_while_empty (): is_empty = %d\n", is_empty);
+#endif
+
+	if (is_empty) {
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("tq_wait_while_empty (): SLEEP - about to call cond wait\n");
+#endif
+
+
+#if __WIN32__
+	  WaitForSingleObject (wake_up_event, INFINITE);
+
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("tq_wait_while_empty (): returned from wait\n");
+#endif
+
+#else
+	  err = pthread_mutex_lock (&wake_up_mutex);
+	  if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("tq_wait_while_empty: pthread_mutex_lock wu err=%d", err);
+	    perror ("");
+	    exit (1);
+	  }
+
+	  xmit_thread_is_waiting = 1;
+	  err = pthread_cond_wait (&wake_up_cond, &wake_up_mutex);
+	  xmit_thread_is_waiting = 0;
+
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("tq_wait_while_empty (): WOKE UP - returned from cond wait, err = %d\n", err);
+#endif
+
+	  if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("tq_wait_while_empty: pthread_cond_wait err=%d", err);
+	    perror ("");
+	    exit (1);
+	  }
+
+	  err = pthread_mutex_unlock (&wake_up_mutex);
+	  if (err != 0) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("tq_wait_while_empty: pthread_mutex_unlock wu err=%d", err);
+	    perror ("");
+	    exit (1);
+	  }
+
+#endif
+	}
+
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_wait_while_empty () returns\n");
+#endif
+
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        tq_remove
+ *
+ * Purpose:     Remove a packet from the head of the specified transmit queue.
+ *
+ * Inputs:	chan	- Channel, 0 is first.
+ *
+ *		prio	- Priority, use TQ_PRIO_0_HI or TQ_PRIO_1_LO.
+ *
+ * Returns:	Pointer to packet object.
+ *		Caller should destroy it with ax25_delete when finished with it.	
+ *
+ *--------------------------------------------------------------------*/
+
+packet_t tq_remove (int chan, int prio)
+{
+
+	packet_t result_p;
+	int err;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_remove(%d,%d) enter critical section\n", chan, prio);
+#endif
+
+#if __WIN32__
+	EnterCriticalSection (&tq_cs);
+#else
+	err = pthread_mutex_lock (&tq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("tq_remove: pthread_mutex_lock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+	if (queue_head[chan][prio] == NULL) {
+
+	  result_p = NULL;
+	}
+	else {
+
+	  result_p = queue_head[chan][prio];
+	  queue_head[chan][prio] = ax25_get_nextp(result_p);
+	  ax25_set_nextp (result_p, NULL);
+	}
+	 
+#if __WIN32__
+	LeaveCriticalSection (&tq_cs);
+#else
+	err = pthread_mutex_unlock (&tq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("tq_remove: pthread_mutex_unlock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_remove(%d,%d) leave critical section, returns %p\n", chan, prio, result_p);
+#endif
+	return (result_p);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        tq_is_empty
+ *
+ * Purpose:     Test queue is empty.
+ *
+ * Inputs:	None - this applies to all channels and priorities.
+ *
+ * Returns:	True if nothing in the queue.	
+ *
+ *--------------------------------------------------------------------*/
+
+static int tq_is_empty (void)
+{
+	int c, p;
+
+
+	for (c=0; c<tq_num_channels; c++) {
+	  for (p=0; p<TQ_NUM_PRIO; p++) {
+
+	    assert (c >= 0 && c < MAX_CHANS);
+	    assert (p >= 0 && p < TQ_NUM_PRIO);
+
+	    if (queue_head[c][p] != NULL)
+	       return (0);
+	  }
+	}
+	return (1);
+
+} /* end tq_is_empty */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        tq_count
+ *
+ * Purpose:     Return count of the number of packets in the specified transmit queue.
+ *
+ * Inputs:	chan	- Channel, 0 is first.
+ *
+ *		prio	- Priority, use TQ_PRIO_0_HI or TQ_PRIO_1_LO.
+ *
+ * Returns:	Number of items in specified queue.	
+ *
+ *--------------------------------------------------------------------*/
+
+int tq_count (int chan, int prio)
+{
+
+	packet_t p;
+	int n;
+
+
+/* Don't bother with critical section. */
+/* Only used for debugging a problem. */
+
+	n = 0;
+	p = queue_head[chan][prio];
+	while (p != NULL) {
+	  n++;
+	  p = ax25_get_nextp(p);
+	}
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("tq_count(%d,%d) returns %d\n", chan, prio, n);
+#endif
+
+	return (n);
+
+} /* end tq_count */
+
+/* end tq.c */
diff --git a/tq.h b/tq.h
new file mode 100755
index 0000000..c23173e
--- /dev/null
+++ b/tq.h
@@ -0,0 +1,35 @@
+
+/*------------------------------------------------------------------
+ *
+ * Module:      tq.h
+ *
+ * Purpose:   	Transmit queue - hold packets for transmission until the channel is clear.
+ *		
+ *---------------------------------------------------------------*/
+
+#ifndef TQ_H
+#define TQ_H 1
+
+#include "ax25_pad.h"
+#include "audio.h"
+
+#define TQ_NUM_PRIO 2				/* Number of priorities. */
+
+#define TQ_PRIO_0_HI 0
+#define TQ_PRIO_1_LO 1
+
+
+
+void tq_init (int nchan);
+
+void tq_append (int chan, int prio, packet_t pp);
+
+void tq_wait_while_empty (void);
+
+packet_t tq_remove (int chan, int prio);
+
+int tq_count (int chan, int prio);
+
+#endif
+
+/* end tq.h */
diff --git a/tt_text.c b/tt_text.c
new file mode 100755
index 0000000..a7be809
--- /dev/null
+++ b/tt_text.c
@@ -0,0 +1,677 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+/*------------------------------------------------------------------
+ *
+ * Module:      tt-text.c
+ *
+ * Purpose:   	Translate between text and touch tone representation.
+ *		
+ * Description: Letters can be represented by different touch tone
+ *		keypad sequences.
+ *
+ * References:	This is based upon APRStt (TM) documents but not 100%
+ *		compliant due to ambiguities and inconsistencies in
+ *		the specifications.
+ *
+ *		http://www.aprs.org/aprstt.html
+ *
+ *---------------------------------------------------------------*/
+
+/*
+ * There are two different encodings called:
+ *
+ *   * Two-key
+ *
+ *		Digits are represented by a single key press.
+ *		Letters (or space) are represented by the corresponding
+ *		key followed by A, B, C, or D depending on the position
+ *		of the letter.
+ *
+ *   * Multi-press
+ *
+ *		Letters are represented by one or more key presses
+ *		depending on their position.
+ *		e.g. on 5/JKL key, J = 1 press, K = 2, etc. 
+ *		The digit is the number of letters plus 1.
+ *		In this case, press 5 key four times to get digit 5.
+ *		When two characters in a row use the same key,
+ *		use the "A" key as a separator.
+ *
+ * Examples:
+ *
+ *	Character	Multipress	Two Key		Comments
+ *	---------	----------	-------		--------
+ *	0		00		0		Space is handled like a letter.
+ *	1		1		1		No letters on 1 button.
+ *	2		2222		2		3 letters -> 4 key presses
+ *	9		99999		9
+ *	W		9		9A
+ *	X		99		9B
+ *	Y		999		9C
+ *	Z		9999		9D
+ *	space		0		0A		0A was used in an APRStt comment example.
+ *
+ *
+ * Note that letters can occur in callsigns and comments.
+ * Everywhere else they are simply digits.
+ */
+
+/*
+ * Everything is based on this table.
+ * Changing it will change everything.
+ */
+
+static const char translate[10][4] = {
+		/*	 A	 B	 C	 D  */
+		/*	---	---	---	--- */
+	/* 0 */	{	' ',	 0,	 0,	 0  },
+	/* 1 */	{	 0,	 0,	 0,	 0  },
+	/* 2 */	{	'A',	'B',	'C',	 0  },
+	/* 3 */	{	'D',	'E',	'F',	 0  },
+	/* 4 */	{	'G',	'H',	'I',	 0  },
+	/* 5 */	{	'J',	'K',	'L',	 0  },
+	/* 6 */	{	'M',	'N',	'O',	 0  },
+	/* 7 */	{	'P',	'Q',	'R',	'S' },
+	/* 8 */	{	'T',	'U',	'V',	 0  },
+	/* 9 */	{	'W',	'X',	'Y',	'Z' } };
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include "textcolor.h"
+
+
+#if defined(ENC_MAIN) || defined(DEC_MAIN)
+
+void text_color_set (dw_color_t c) { return; }
+
+int dw_printf (const char *fmt, ...) 
+{
+	va_list args;
+	int len;
+	
+	va_start (args, fmt);
+	len = vprintf (fmt, args);
+	va_end (args);
+	return (len);
+}
+
+#endif
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        tt_text_to_multipress
+ *
+ * Purpose:     Convert text to the multi-press representation.
+ *
+ * Inputs:      text	- Input string.
+ *			  Should contain only digits, letters, or space.
+ *			  All other punctuation is treated as space.
+ *
+ *		quiet	- True to suppress error messages.
+ *	
+ * Outputs:	buttons	- Sequence of buttons to press.
+ *
+ * Returns:     Number of errors detected.
+ *
+ *----------------------------------------------------------------*/
+
+int tt_text_to_multipress (char *text, int quiet, char *buttons) 
+{
+	char *t = text;
+	char *b = buttons;
+	char c;
+	int row, col;
+	int errors = 0;
+	int found;
+	int n;
+
+	*b = '\0';
+	
+	while ((c = *t++) != '\0') {
+
+	  if (isdigit(c)) {
+	
+/* Count number of other characters assigned to this button. */
+/* Press that number plus one more. */
+
+	    n = 1;
+	    row = c - '0';
+	    for (col=0; col<4; col++) {
+	      if (translate[row][col] != 0) {
+	        n++;
+	      }
+	    }
+	    if (buttons[0] != '\0' && *(b-1) == row + '0') {
+	      *b++ = 'A';
+	    }
+	    while (n--) {
+	      *b++ = row + '0';
+	      *b = '\0';
+	    }
+	  }
+	  else {
+	    if (isupper(c)) {
+	      ;
+	    }	  
+	    else if (islower(c)) {
+	      c = toupper(c);
+	    }
+	    else if (c != ' ') {
+	      errors++;
+	      if (! quiet) {
+	        text_color_set (DW_COLOR_ERROR);
+		dw_printf ("Text to multi-press: Only letters, digits, and space allowed.\n");
+	      }
+	      c = ' ';
+	    }
+
+/* Search for everything else in the translation table. */
+/* Press number of times depending on column where found. */
+
+	    found = 0;
+
+	    for (row=0; row<10 && ! found; row++) {
+	      for (col=0; col<4 && ! found; col++) {
+	        if (c == translate[row][col]) {
+
+/* Stick in 'A' if previous character used same button. */
+
+	          if (buttons[0] != '\0' && *(b-1) == row + '0') {
+	            *b++ = 'A';
+	          }
+	          n = col + 1;
+	          while (n--) {
+	            *b++ = row + '0';
+	            *b = '\0';
+	            found = 1;
+	          }
+	        }
+	      }
+	    }
+	    if (! found) {
+	      errors++;
+	      text_color_set (DW_COLOR_ERROR);
+	      dw_printf ("Text to multi-press: INTERNAL ERROR.  Should not be here.\n");
+	    }
+	  }
+	}
+	return (errors);          
+
+} /* end tt_text_to_multipress */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        tt_text_to_two_key
+ *
+ * Purpose:     Convert text to the two-key representation.
+ *
+ * Inputs:      text	- Input string.
+ *			  Should contain only digits, letters, or space.
+ *			  All other punctuation is treated as space.
+ *
+ *		quiet	- True to suppress error messages.
+ *	
+ * Outputs:	buttons	- Sequence of buttons to press.
+ *
+ * Returns:     Number of errors detected.
+ *
+ *----------------------------------------------------------------*/
+
+int tt_text_to_two_key (char *text, int quiet, char *buttons) 
+{
+	char *t = text;
+	char *b = buttons;
+	char c;
+	int row, col;
+	int errors = 0;
+	int found;
+
+
+	*b = '\0';
+	
+	while ((c = *t++) != '\0') {
+
+	  if (isdigit(c)) {
+	
+/* Digit is single key press. */
+	  
+	    *b++ = c;
+	    *b = '\0';
+	  }
+	  else {
+	    if (isupper(c)) {
+	      ;
+	    }	  
+	    else if (islower(c)) {
+	      c = toupper(c);
+	    }
+	    else if (c != ' ') {
+	      errors++;
+	      if (! quiet) {
+	        text_color_set (DW_COLOR_ERROR);
+		dw_printf ("Text to two key: Only letters, digits, and space allowed.\n");
+	      }
+	      c = ' ';
+	    }
+
+/* Search for everything else in the translation table. */
+
+	    found = 0;
+
+	    for (row=0; row<10 && ! found; row++) {
+	      for (col=0; col<4 && ! found; col++) {
+	        if (c == translate[row][col]) {
+		  *b++ = '0' + row;
+	          *b++ = 'A' + col;
+	          *b = '\0';
+	          found = 1;
+	        }
+	      }
+	    }
+	    if (! found) {
+	      errors++;
+	      text_color_set (DW_COLOR_ERROR);
+	      dw_printf ("Text to two-key: INTERNAL ERROR.  Should not be here.\n");
+	    }
+	  }
+	}
+	return (errors);          
+
+} /* end tt_text_to_two_key */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        tt_multipress_to_text
+ *
+ * Purpose:     Convert the multi-press representation to text.
+ *
+ * Inputs:      buttons	- Input string.
+ *			  Should contain only 0123456789A.
+ *
+ *		quiet	- True to suppress error messages.
+ *	
+ * Outputs:	text	- Converted to letters, digits, space.
+ *
+ * Returns:     Number of errors detected.
+ *
+ *----------------------------------------------------------------*/
+
+int tt_multipress_to_text (char *buttons, int quiet, char *text) 
+{
+	char *b = buttons;
+	char *t = text;
+	char c;
+	int row, col;
+	int errors = 0;
+	int maxspan;
+	int n;
+
+	*t = '\0';
+	
+	while ((c = *b++) != '\0') {
+
+	  if (isdigit(c)) {
+	
+/* Determine max that can occur in a row. */
+/* = number of other characters assigned to this button + 1. */
+
+	    maxspan = 1;
+	    row = c - '0';
+	    for (col=0; col<4; col++) {
+	      if (translate[row][col] != 0) {
+	        maxspan++;
+	      }
+	    }
+
+/* Count number of consecutive same digits. */
+
+	    n = 1;
+	    while (c == *b) {
+	      b++;
+	      n++;
+	    }
+
+	    if (n < maxspan) {
+	      *t++ = translate[row][n-1];
+	      *t = '\0';
+	    }
+	    else if (n == maxspan) {
+	      *t++ = c;
+	      *t = '\0';
+	    }
+	    else {
+	      errors++;
+	      if (! quiet) {
+	        text_color_set (DW_COLOR_ERROR);
+	        dw_printf ("Multi-press to text: Maximum of %d \"%c\" can occur in a row.\n", maxspan, c);
+	      }
+	      /* Treat like the maximum length. */
+	      *t++ = c;
+	      *t = '\0';
+	    }
+	  }
+	  else if (c == 'A' || c == 'a') {
+
+/* Separator should occur only if digit before and after are the same. */
+	     
+	    if (b == buttons + 1 || *b == '\0' || *(b-2) != *b) {
+	      errors++;
+	      if (! quiet) {
+	        text_color_set (DW_COLOR_ERROR);
+	        dw_printf ("Multi-press to text: \"A\" can occur only between two same digits.\n");
+  	      }
+	    }
+	  }
+	  else {
+
+/* Completely unexpected character. */
+
+	    errors++;
+	    if (! quiet) {
+	      text_color_set (DW_COLOR_ERROR);
+	      dw_printf ("Multi-press to text: \"%c\" not allowed.\n", c);
+  	    }
+	  }
+	}
+	return (errors);          
+
+} /* end tt_multipress_to_text */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        tt_two_key_to_text
+ *
+ * Purpose:     Convert the two key representation to text.
+ *
+ * Inputs:      buttons	- Input string.
+ *			  Should contain only 0123456789ABCD.
+ *
+ *		quiet	- True to suppress error messages.
+ *	
+ * Outputs:	text	- Converted to letters, digits, space.
+ *
+ * Returns:     Number of errors detected.
+ *
+ *----------------------------------------------------------------*/
+
+int tt_two_key_to_text (char *buttons, int quiet, char *text) 
+{
+	char *b = buttons;
+	char *t = text;
+	char c;
+	int row, col;
+	int errors = 0;
+	int n;
+
+	*t = '\0';
+	
+	while ((c = *b++) != '\0') {
+
+	  if (isdigit(c)) {
+	
+/* Letter (or space) if followed by ABCD. */
+	    
+	    row = c - '0';
+	    col = -1;
+
+	    if (*b >= 'A' && *b <= 'D') {
+	      col = *b++ - 'A';
+	    }
+	    else if (*b >= 'a' && *b <= 'd') {
+	      col = *b++ - 'a';
+	    }
+
+	    if (col >= 0) {
+	      if (translate[row][col] != 0) {
+	        *t++ = translate[row][col];
+	        *t = '\0';
+	      }
+	      else {
+		errors++;
+	        if (! quiet) {
+	          text_color_set (DW_COLOR_ERROR);
+	          dw_printf ("Two key to text: Invalid combination \"%c%c\".\n", c, col+'A');
+		}
+	      }
+	    }
+	    else {
+	      *t++ = c;
+	      *t = '\0';
+	    }
+	  }
+	  else if ((c >= 'A' && c <= 'D') || (c >= 'a' && c <= 'd')) {
+
+/* ABCD not expected here. */
+	     
+	    errors++;
+	    if (! quiet) {
+	      text_color_set (DW_COLOR_ERROR);
+	      dw_printf ("Two-key to text: A, B, C, or D in unexpected location.\n");
+	    }
+	  }
+	  else {
+
+/* Completely unexpected character. */
+
+	    errors++;
+	    if (! quiet) {
+	      text_color_set (DW_COLOR_ERROR);
+	      dw_printf ("Two-key to text: Invalid character \"%c\".\n", c);
+  	    }
+	  }
+	}
+	return (errors);          
+
+} /* end tt_two_key_to_text */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        tt_guess_type
+ *
+ * Purpose:     Try to guess which encoding we have.
+ *
+ * Inputs:      buttons	- Input string.
+ *			  Should contain only 0123456789ABCD.
+ *
+ * Returns:     TT_MULTIPRESS	- Looks like multipress.
+ *		TT_TWO_KEY	- Looks like two key.
+ *		TT_EITHER	- Could be either one.
+ *
+ *----------------------------------------------------------------*/
+
+typedef enum { TT_EITHER, TT_MULTIPRESS, TT_TWO_KEY } tt_enc_t;
+
+tt_enc_t tt_guess_type (char *buttons) 
+{
+	char text[256];
+	int err_mp;
+	int err_tk;
+	
+/* If it contains B, C, or D, it can't be multipress. */
+
+	if (strchr (buttons, 'B') != NULL || strchr (buttons, 'b') != NULL ||
+	    strchr (buttons, 'C') != NULL || strchr (buttons, 'c') != NULL ||
+	    strchr (buttons, 'D') != NULL || strchr (buttons, 'd') != NULL) {
+	  return (TT_TWO_KEY);
+	}
+
+/* Try parsing quietly and see if one gets errors and the other doesn't. */
+
+	err_mp = tt_multipress_to_text (buttons, 1, text);
+	err_tk = tt_two_key_to_text (buttons, 1, text);
+
+	if (err_mp == 0 && err_tk > 0) {
+	  return (TT_MULTIPRESS);
+ 	}
+	else if (err_tk == 0 && err_mp > 0) {
+	  return (TT_TWO_KEY);
+	}
+
+/* Could be either one. */
+
+	return (TT_EITHER);
+
+} /* end tt_guess_type */
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        main
+ *
+ * Purpose:     Utility program for testing the encoding.
+ *
+ *----------------------------------------------------------------*/
+
+
+#if ENC_MAIN
+
+int checksum (char *tt)
+{
+	int cs = 10;	/* Assume leading 'A'. */
+			/* Doesn't matter due to mod 10 at the end. */
+	char *p;
+
+	for (p = tt; *p != '\0'; p++) {
+	  if (isdigit(*p)) {
+	    cs += *p - '0';
+	  }
+	  else if (isupper(*p)) {
+	    cs += *p - 'A' + 10;
+	  }
+	  else if (islower(*p)) {
+	    cs += *p - 'a' + 10;
+	  }
+	}
+
+	return (cs % 10);
+}
+
+int main (int argc, char *argv[])
+{
+	char text[1000], buttons[2000];
+	int n;
+	int cs;
+
+	text_color_set (DW_COLOR_INFO);
+
+	if (argc < 2) {
+	  text_color_set (DW_COLOR_ERROR);
+	  dw_printf ("Supply text string on command line.\n");
+	  exit (1);
+	}
+
+	strcpy (text, argv[1]);
+
+	for (n = 2; n < argc; n++) {
+	  strcat (text, " ");
+	  strcat (text, argv[n]);
+	}
+
+	dw_printf ("Push buttons for multi-press method:\n");
+	n = tt_text_to_multipress (text, 0, buttons);
+	cs = checksum (buttons);
+
+	dw_printf ("\"%s\"    checksum for call = %d\n", buttons, cs);
+
+	dw_printf ("Push buttons for two-key method:\n");
+	n = tt_text_to_two_key (text, 0, buttons);
+	cs = checksum (buttons);
+
+	dw_printf ("\"%s\"    checksum for call = %d\n", buttons, cs);
+
+	return(0);
+
+}  /* end main */
+
+#endif		/* encoding */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        main
+ *
+ * Purpose:     Utility program for testing the decoding.
+ *
+ *----------------------------------------------------------------*/
+
+
+#if DEC_MAIN
+
+
+int main (int argc, char *argv[])
+{
+	char buttons[2000], text[1000];
+	int n;
+
+	text_color_set (DW_COLOR_INFO);
+
+	if (argc < 2) {
+	  text_color_set (DW_COLOR_ERROR);
+	  dw_printf ("Supply button sequence on command line.\n");
+	  exit (1);
+	}
+
+	strcpy (buttons, argv[1]);
+
+	for (n = 2; n < argc; n++) {
+	  strcat (buttons, argv[n]);
+	}
+
+	switch (tt_guess_type(buttons)) {
+	  case TT_MULTIPRESS:
+	    dw_printf ("Looks like multi-press encoding.\n");
+	    break;
+	  case TT_TWO_KEY:
+	    dw_printf ("Looks like two-key encoding.\n");
+	    break;
+	  default:
+	    dw_printf ("Could be either type of encoding.\n");
+	    break;
+	}
+
+	dw_printf ("Decoded text from multi-press method:\n");
+	n = tt_multipress_to_text (buttons, 0, text);
+	dw_printf ("\"%s\"\n", text);
+
+	dw_printf ("Decoded text from two-key method:\n");
+	n = tt_two_key_to_text (buttons, 0, text);
+	dw_printf ("\"%s\"\n", text);
+
+	return(0);
+
+}  /* end main */
+
+#endif		/* decoding */
+
+
+
+/* end tt-text.c */
+
diff --git a/tt_text.h b/tt_text.h
new file mode 100755
index 0000000..d2265d8
--- /dev/null
+++ b/tt_text.h
@@ -0,0 +1,17 @@
+
+/* tt_text.h */
+
+
+int tt_text_to_multipress (char *text, int quiet, char *buttons); 
+
+
+int tt_text_to_two_key (char *text, int quiet, char *buttons); 
+
+
+int tt_multipress_to_text (char *buttons, int quiet, char *text); 
+
+
+int tt_two_key_to_text (char *buttons, int quiet, char *text);
+
+
+/* end tt_text.h */
\ No newline at end of file
diff --git a/tt_user.c b/tt_user.c
new file mode 100755
index 0000000..5a3439a
--- /dev/null
+++ b/tt_user.c
@@ -0,0 +1,806 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+/*------------------------------------------------------------------
+ *
+ * Module:      tt-user.c
+ *
+ * Purpose:   	Keep track of the APRStt users.
+ *		
+ * Description: This maintains a list of recently heard APRStt users
+ *		and prepares "object" format packets for transmission.
+ *
+ * References:	This is based upon APRStt (TM) documents but not 100%
+ *		compliant due to ambiguities and inconsistencies in
+ *		the specifications.
+ *
+ *		http://www.aprs.org/aprstt.html
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <assert.h>
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "aprs_tt.h"
+#include "tt_text.h"
+#include "dedupe.h"
+#include "tq.h"
+#include "igate.h"
+#include "tt_user.h"
+#include "encode_aprs.h"
+#include "latlong.h"
+
+/* 
+ * Information kept about local APRStt users.
+ *
+ * For now, just use a fixed size array for simplicity.
+ */
+
+#if TT_MAIN
+#define MAX_TT_USERS 3		
+#else
+#define MAX_TT_USERS 100
+#endif
+
+#define MAX_CALLSIGN_LEN 9	/* "Object Report" names can be up to 9 characters. */
+				
+#define MAX_COMMENT_LEN 43	/* Max length of comment in "Object Report." */
+
+//#define G_UNKNOWN -999999	/* Should be in one place. */
+
+#define NUM_XMITS 3
+#define XMIT_DELAY_1 5
+#define XMIT_DELAY_2 8
+#define XMIT_DELAY_3 13
+
+
+static struct tt_user_s {
+
+	char callsign[MAX_CALLSIGN_LEN+1];	/* Callsign of station heard. */
+						/* Does not include the "-12" SSID added later. */
+						/* Possibly other tactical call / object label. */
+						/* Null string indicates table position is not used. */
+
+	int ssid;				/* SSID to add. */	
+						/* Default of 12 but not always. */			
+		
+	char overlay;				/* Overlay character. Should be 0-9, A-Z. */
+						/* Could be / or \ for general object. */
+
+	char symbol;				/* 'A' for traditional.  */
+						/* Can be any symbol for extended objects. */
+
+	char digit_suffix[3+1];			/* Suffix abbreviation as 3 digits. */
+
+	time_t last_heard;			/* Timestamp when last heard.  */
+						/* User information will be deleted at some */
+						/* point after last time being heard. */
+
+	int xmits;				/* Number of remaining times to transmit info */
+						/* about the user.   This is set to 3 when */
+						/* a station is heard and decremented each time */
+						/* an object packet is sent.  The idea is to send */
+						/* 3 within 30 seconds to improve chances of */
+						/* being heard while using digipeater duplicate */
+						/* removal. */
+
+	time_t next_xmit;			/* Time for next transmit.  Meaningful only */
+						/* if xmits > 0. */
+
+	int corral_slot;			/* If location is known, set this to 0. */
+						/* Otherwise, this is a display offset position */
+						/* from the gateway. */
+
+	double latitude, longitude;		/* Location either from user or generated */		
+						/* position in the corral. */
+
+	char freq[12];				/* Frequency in format 999.999MHz */
+
+	char comment[MAX_COMMENT_LEN+1];	/* Free form comment. */
+
+	char mic_e;				/* Position status. */
+
+	char dao[8];				/* Enhanced position information. */
+
+} tt_user[MAX_TT_USERS];
+
+
+static void clear_user(int i);
+
+static void xmit_object_report (int i, double c_lat, double c_long, int ambiguity, double c_offs);
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        tt_user_init
+ *
+ * Purpose:     Initialize the APRStt gateway at system startup time.
+ *
+ * Inputs:      Configuration options gathered by config.c.
+ *
+ * Global out:	Make our own local copy of the structure here.
+ *
+ * Returns:     None
+ *
+ * Description:	The main program needs to call this at application
+ *		start up time after reading the configuration file.
+ *
+ *		TT_MAIN is defined for unit testing.
+ *
+ *----------------------------------------------------------------*/
+
+static struct tt_config_s tt_config;
+
+
+void tt_user_init (struct tt_config_s *p)
+{
+	int i;
+
+#if TT_MAIN
+	/* For unit testing. */
+
+	memset (&tt_config, 0, sizeof(struct tt_config_s));	
+
+	/* Don't care about the location translation here. */
+
+	tt_config.retain_time = 20;		/* Normally 80 minutes. */
+	tt_config.num_xmits = 3;
+	assert (tt_config.num_xmits <= TT_MAX_XMITS);
+	tt_config.xmit_delay[0] = 3;		/* Before initial transmission. */
+	tt_config.xmit_delay[1] = 5;
+	tt_config.xmit_delay[2] = 5;
+
+	tt_config.corral_lat = 42.61900;
+	tt_config.corral_lon = -71.34717;
+	tt_config.corral_offset = 0.02 / 60;
+	tt_config.corral_ambiguity = 0;
+
+#else
+	memcpy (&tt_config, p, sizeof(struct tt_config_s));
+#endif
+
+	for (i=0; i<MAX_TT_USERS; i++) {
+	  clear_user (i);
+	}
+}
+
+/*------------------------------------------------------------------
+ *
+ * Name:        tt_user_search
+ *
+ * Purpose:     Search for user in recent history.
+ *
+ * Inputs:      callsign	- full or a suffix abbreviation
+ *		overlay
+ *
+ * Returns:     Handle for refering to table position or -1 if not found.
+ *		This happens to be an index into an array but
+ *		the implementation could change so the caller should 
+ *		not make any assumptions.
+ *
+ *----------------------------------------------------------------*/
+
+int tt_user_search (char *callsign, char overlay)
+{
+	int i;
+/*
+ * First, look for exact match to full call and overlay.
+ */
+	for (i=0; i<MAX_TT_USERS; i++) {
+	  if (strcmp(callsign, tt_user[i].callsign) == 0 && 
+		overlay == tt_user[i].overlay) {
+	    return (i);
+	  }
+	}
+
+/*
+ * Look for digits only suffix plus overlay.
+ */
+	for (i=0; i<MAX_TT_USERS; i++) {
+	  if (strcmp(callsign, tt_user[i].digit_suffix) == 0 && 
+		overlay != ' ' &&
+		overlay == tt_user[i].overlay) {
+	    return (i);
+	  }
+	}
+
+/*
+ * Look for digits only suffix if no overlay was specified.
+ */
+	for (i=0; i<MAX_TT_USERS; i++) {
+	  if (strcmp(callsign, tt_user[i].digit_suffix) == 0 && 
+		overlay == ' ') {
+	    return (i);
+	  }
+	}
+
+/*
+ * Not sure about the new spelled suffix yet...
+ */
+	return (-1);
+
+}  /* end tt_user_search */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        clear_user
+ *
+ * Purpose:     Clear specified user table entry.
+ *
+ * Inputs:      handle for user table entry.
+ *
+ *----------------------------------------------------------------*/
+
+static void clear_user(int i)
+{
+	assert (i >= 0 && i < MAX_TT_USERS);
+
+	memset (&tt_user[i], 0, sizeof (struct tt_user_s));
+
+} /* end clear_user */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        find_avail
+ *
+ * Purpose:     Find an available user table location.
+ *
+ * Inputs:      none
+ *
+ * Returns:     Handle for refering to table position.
+ *
+ * Description:	If table is already full, this should delete the 
+ *		least recently heard user to make room.		
+ *
+ *----------------------------------------------------------------*/
+
+static int find_avail (void)
+{
+	int i;
+	int i_oldest;
+
+	for (i=0; i<MAX_TT_USERS; i++) {
+	  if (tt_user[i].callsign[0] == '\0') {
+	    clear_user (i);
+	    return (i);
+	  }
+	}
+
+/* Remove least recently heard. */
+
+	i_oldest = 0;
+
+	for (i=1; i<MAX_TT_USERS; i++) {
+	  if (tt_user[i].last_heard < tt_user[i_oldest].last_heard) {
+	    i_oldest = i;
+	  }
+	}
+
+	clear_user (i_oldest);
+	return (i_oldest);
+
+} /* end find_avail */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        corral_slot
+ *
+ * Purpose:     Find an available position in the corral.
+ *
+ * Inputs:      none
+ *
+ * Returns:     Small integer >= 1 not already in use.
+ *
+ *----------------------------------------------------------------*/
+
+static int corral_slot (void)
+{
+	int slot, i, used;
+
+	for (slot=1; ; slot++) {
+	  used = 0;;
+	  for (i=0; i<MAX_TT_USERS && ! used; i++) {
+	    if (tt_user[i].callsign[0] != '\0' && tt_user[i].corral_slot == slot) {
+	      used = 1;
+	    }
+	  }
+	  if (!used) {
+	    return (slot);
+	  }
+	}
+
+} /* end corral_slot */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        digit_suffix
+ *
+ * Purpose:     Find 3 digit only suffix code for given call.
+ *
+ * Inputs:      callsign
+ *
+ * Outputs:	3 digit suffix
+ *
+ *----------------------------------------------------------------*/
+
+static void digit_suffix (char *callsign, char *suffix)
+{
+	char two_key[50];
+	char *t;
+
+
+	strcpy (suffix, "000");
+	tt_text_to_two_key (callsign, 0, two_key);
+	for (t = two_key; *t != '\0'; t++) {
+	  if (isdigit(*t)) {
+	    suffix[0] = suffix[1];
+	    suffix[1] = suffix[2];
+	    suffix[2] = *t;
+	  }
+	}
+
+
+} /* end digit_suffix */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        tt_user_heard
+ *
+ * Purpose:     Record information from an APRStt trasmission.
+ *
+ * Inputs:      callsign	- full or an abbreviation
+ *		ssid
+ *		overlay		- or symbol table identifier
+ *		symbol
+ *		latitude
+ *		longitude
+ *		freq
+ *		comment
+ *		mic_e
+ *		dao
+ *
+ * Outputs:	Information is stored in table above.
+ *		Last heard time is updated.
+ *		Object Report transmission is scheduled.
+ *
+ * Returns:	0 for success or one of the TT_ERROR_... codes.
+ *
+ *----------------------------------------------------------------*/
+
+int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double latitude, 
+		double longitude, char *freq, char *comment, char mic_e, char *dao)
+{
+	int i;
+	
+/*
+ * At this time all messages are expected to contain a callsign.
+ * Other types of messages, not related to a particular person/object
+ * are a future possibility. 
+ */
+	if (callsign[0] == '\0') {
+	  text_color_set(DW_COLOR_ERROR);
+	  printf ("APRStt message did not include callsign.\n");
+	  return (TT_ERROR_NO_CALL);
+	}
+
+/*
+ * Is it someone new or a returning user?
+ */
+	i = tt_user_search (callsign, overlay);
+	if (i == -1) {
+
+/*
+ * New person.  Create new table entry with all available information.
+ */
+	  i = find_avail ();
+
+	  assert (i >= 0 && i < MAX_TT_USERS);
+	  strncpy (tt_user[i].callsign, callsign, MAX_CALLSIGN_LEN);
+	  tt_user[i].callsign[MAX_CALLSIGN_LEN] = '\0';
+	  tt_user[i].ssid = ssid;
+	  tt_user[i].overlay = overlay;
+	  tt_user[i].symbol = symbol;
+	  digit_suffix(tt_user[i].callsign, tt_user[i].digit_suffix);
+	  if (latitude != G_UNKNOWN && longitude != G_UNKNOWN) {
+	    /* We have specific location. */
+	    tt_user[i].corral_slot = 0;
+	    tt_user[i].latitude = latitude;
+	    tt_user[i].longitude = longitude;
+	  }
+	  else {
+	    /* Unknown location, put it in the corral. */
+	    tt_user[i].corral_slot = corral_slot();
+	  }
+
+	  strcpy (tt_user[i].freq, freq);
+	  strncpy (tt_user[i].comment, comment, MAX_COMMENT_LEN);
+	  tt_user[i].comment[MAX_COMMENT_LEN] = '\0';
+	  tt_user[i].mic_e = mic_e;
+	  strncpy(tt_user[i].dao, dao, 6);
+	}
+	else {
+/*
+ * Known user.  Update with any new information.
+ */
+	  assert (i >= 0 && i < MAX_TT_USERS);
+
+	  /* Any reason to look at ssid here? */
+
+	  if (latitude != G_UNKNOWN && longitude != G_UNKNOWN) {
+	    /* We have specific location. */
+	    tt_user[i].corral_slot = 0;
+	    tt_user[i].latitude = latitude;
+	    tt_user[i].longitude = longitude;
+	  }
+
+	  if (freq[0] != '\0') {
+	    strcpy (tt_user[i].freq, freq);
+	  }
+
+	  if (comment[0] != '\0') {
+	    strncpy (tt_user[i].comment, comment, MAX_COMMENT_LEN);
+	    tt_user[i].comment[MAX_COMMENT_LEN] = '\0';
+	  }
+
+	  if (mic_e != ' ') {
+	    tt_user[i].mic_e = mic_e;
+	  }
+	  if (strlen(dao) > 0) {
+	    strncpy(tt_user[i].dao, dao, 6);
+	    tt_user[i].dao[5] = '\0';
+	  }
+	}
+
+/*
+ * In both cases, note last time heard and schedule object report transmission. 
+ */
+	tt_user[i].last_heard = time(NULL);
+	tt_user[i].xmits = 0;
+	tt_user[i].next_xmit = tt_user[i].last_heard + tt_config.xmit_delay[0];
+
+	return (0);	/* Success! */
+
+} /* end tt_user_heard */
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        tt_user_background
+ *
+ * Purpose:     
+ *
+ * Inputs:      
+ *
+ * Outputs:	Append to transmit queue.
+ *
+ * Returns:     None
+ *
+ * Description:	...... TBD
+ *
+ *----------------------------------------------------------------*/
+
+void tt_user_background (void)
+{
+	time_t now = time(NULL);
+	int i;
+
+	for (i=0; i<MAX_TT_USERS; i++) {
+	  if (tt_user[i].callsign[0] != '\0') {
+	    if (tt_user[i].xmits < tt_config.num_xmits && tt_user[i].next_xmit <= now) {
+
+	      xmit_object_report (i, tt_config.corral_lat, tt_config.corral_lon,
+			tt_config.corral_ambiguity, tt_config.corral_offset);	
+ 
+	      /* Increase count of number times this one was sent. */
+	      tt_user[i].xmits++;
+	      if (tt_user[i].xmits < tt_config.num_xmits) {
+	        /* Schedule next one. */
+	        tt_user[i].next_xmit += tt_config.xmit_delay[tt_user[i].xmits];    
+	      }
+	    }
+	  }
+	}
+
+/*
+ * Purge if too old.
+ */
+	for (i=0; i<MAX_TT_USERS; i++) {
+	  if (tt_user[i].callsign[0] != '\0') {
+	    if (tt_user[i].last_heard + tt_config.retain_time < now) {
+
+//TODO: remove printf ("debug: purging expired user %d\n", i);
+
+	      clear_user (i);
+	    }
+	  }
+	}
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        xmit_object_report
+ *
+ * Purpose:     Create object report packet and put into transmit queue.
+ *
+ * Inputs:      i	- Index into user table.
+ *		c_lat	- Corral latitude.
+ *		c_long	- Corral longitude.
+ *		ambiguity - Number of amibiguity digits: 0, 1, 2, or 3.
+ *		c_offs	- Corral (latitude) offset.
+ *
+ * Outputs:	Append to transmit queue.
+ *
+ * Returns:     None
+ *
+ * Description:	Details for specified user are converted to
+ *		"Object Report Format" and added to the transmit queue.
+ *
+ *		If the user did not report a position, we have to make 
+ *		up something so the corresponding object will show up on
+ *		the map or other list of nearby stations.
+ *
+ *		The traditional approach is to put them in different 
+ *		positions in the "corral" by applying increments of an
+ *		offset from the starting position.  This has two 
+ *		unfortunate properties.  It gives the illusion we know
+ *		where the person is located.   Being in the ,,,
+ *
+ *----------------------------------------------------------------*/
+
+static const char *mic_e_position_comment[10] = {
+	"",
+	"/off duty  ",
+	"/enroute   ",
+	"/in service",
+	"/returning ",
+	"/committed ",
+	"/special   ",
+	"/priority  ",
+	"/emergency ",
+	"/custom 1  " };
+
+static void xmit_object_report (int i, double c_lat, double c_long, int ambiguity, double c_offs)
+{
+	char object_name[20];
+	char object_info[50];
+	char info_comment[100];
+	double olat, olong;
+	char stemp[200];
+	packet_t pp;
+	unsigned char fbuf[AX25_MAX_PACKET_LEN];
+	int flen;
+
+
+	assert (i >= 0 && i < MAX_TT_USERS);
+
+/*
+ * Prepare the object name.  
+ * Tack on "-12" if it is a callsign.
+ */
+	strcpy (object_name, tt_user[i].callsign);
+
+	if (strlen(object_name) <= 6 && tt_user[i].ssid != 0) {
+	  char stemp8[8];
+	  sprintf (stemp8, "-%d", tt_user[i].ssid);
+	  strcat (object_name, stemp8);
+	}
+
+	if (tt_user[i].corral_slot == 0) {
+/* 
+ * Known location.
+ */
+	  olat = tt_user[i].latitude;
+	  olong = tt_user[i].longitude;
+	}
+	else {
+/*
+ * Use made up position in the corral.
+ */
+	  olat = c_lat - (tt_user[i].corral_slot - 1) * c_offs;
+	  olong = c_long;
+	}
+
+/*
+ * Build comment field from various information.
+ */
+	strcpy (info_comment, "");
+
+	if (strlen(tt_user[i].comment) != 0) {
+	  strcat (info_comment, tt_user[i].comment);
+	}
+	if (tt_user[i].mic_e >= '1' && tt_user[i].mic_e <= '9') {
+	  strcat (info_comment, mic_e_position_comment[tt_user[i].mic_e - '0']);
+	}
+	if (strlen(tt_user[i].dao) > 0) {
+	  strcat (info_comment, tt_user[i].dao);
+	}
+
+	/* Official limit is 43 characters. */
+	info_comment[MAX_COMMENT_LEN] = '\0';
+	
+/*
+ * Combine with header from configuration file.  
+ *
+ * (If APRStt gateway has been configured.)
+ */
+	if (tt_config.obj_xmit_header[0] != '\0') {
+
+// TODO: Should take the call from radio channel configuration.
+// Application version is compiled in.
+// Config should have only optional via path.
+
+	  strcpy (stemp, tt_config.obj_xmit_header);
+	  strcat (stemp, ":");
+
+	  encode_object (object_name, 0, tt_user[i].last_heard, olat, olong, 
+		tt_user[i].overlay, tt_user[i].symbol, 
+		0,0,0,NULL, 0,0,	/* PHGD, C/S */
+		atof(tt_user[i].freq), 0, 0, info_comment, object_info);
+
+	  strcat (stemp, object_info);
+
+	  //text_color_set(DW_COLOR_ERROR);
+	  //printf ("\nDEBUG: %s\n\n", stemp);
+
+
+#if TT_MAIN
+
+	  printf ("---> %s\n\n", stemp);
+
+#else
+
+/*
+ * Convert to packet and append to transmit queue.
+ */
+	  pp = ax25_from_text (stemp, 1);
+
+	  flen = ax25_pack (pp, fbuf);
+
+/*
+ * Process as if we heard ourself.
+ */
+	  // TODO:  We need radio channel where this came from.
+	  // It would make a difference if running two radios
+	  // and they have different station identifiers.
+ 
+	  int chan = 0;
+          igate_send_rec_packet (chan, pp);
+
+	  /* Remember it so we don't digipeat our own. */
+
+	  dedupe_remember (pp, tt_config.obj_xmit_chan);
+
+	  tq_append (tt_config.obj_xmit_chan, TQ_PRIO_1_LO, pp);
+#endif 
+	}
+}
+
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        tt_user_dump
+ *
+ * Purpose:     Print information about known users for debugging.
+ *
+ * Inputs:      None.
+ *
+ * Description:	Timestamps displayed relative to current time.
+ *
+ *----------------------------------------------------------------*/
+
+void tt_user_dump (void)
+{
+	int i;
+	time_t now = time(NULL);
+	
+	printf ("call  ov suf lsthrd xmit nxt cor  lat    long freq       m comment\n");
+	for (i=0; i<MAX_TT_USERS; i++) {
+	  if (tt_user[i].callsign[0] != '\0') {
+	    printf ("%-6s %c%c %-3s %6d %d %+6d %d %6.2f %7.2f %-10s %c %s\n",
+	    	tt_user[i].callsign,
+	    	tt_user[i].overlay,
+	    	tt_user[i].symbol,
+	    	tt_user[i].digit_suffix,
+	    	(int)(tt_user[i].last_heard - now),
+	    	tt_user[i].xmits,
+	    	(int)(tt_user[i].next_xmit - now),
+	    	tt_user[i].corral_slot,
+	    	tt_user[i].latitude,
+	    	tt_user[i].longitude,
+	    	tt_user[i].freq,
+	    	tt_user[i].mic_e,
+	    	tt_user[i].comment);
+	  }
+	}
+			
+}
+
+
+/*------------------------------------------------------------------
+ *
+ * Name:        main
+ *
+ * Purpose:     Quick test for some functions in this file.
+ *
+ * Description:	Just a smattering, not an organized test.
+ *
+ * 		$ rm a.exe ; gcc -DTT_MAIN -Iregex tt_user.c tt_text.c encode_aprs.c latlong.c ; ./a.exe
+ *
+ *----------------------------------------------------------------*/
+
+
+#if TT_MAIN
+
+void text_color_set ( enum dw_color_e c )
+{
+        return;
+} 
+
+int main (int argc, char *argv[])
+{
+	int n;
+
+	tt_user_init(NULL);
+
+	tt_user_heard ("TEST1",  12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
+	SLEEP_SEC (1);
+	tt_user_heard ("TEST2",  12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
+	SLEEP_SEC (1);
+	tt_user_heard ("TEST3",  12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
+	SLEEP_SEC (1);
+	tt_user_heard ("TEST4",  12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
+	SLEEP_SEC (1);
+	tt_user_heard ("WB2OSZ", 12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
+	tt_user_heard ("K2H",    12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
+	tt_user_dump ();
+
+	tt_user_heard ("679",    12, 'J', 'A', 37.25,     -71.75,    " ", " ", ' ', "!T99!");
+	tt_user_heard ("WB2OSZ", 12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "146.520MHz", "", ' ', "!T99!");
+	tt_user_heard ("679",    12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "Hello, world", '9', "!T99!");
+	tt_user_dump ();
+	
+	for (n=0; n<30; n++) {
+	  SLEEP_SEC(1);
+	  tt_user_background ();
+	}
+
+	return(0);
+
+}  /* end main */
+
+#endif		/* unit test */
+
+
+/* end tt-user.c */
+
diff --git a/tt_user.h b/tt_user.h
new file mode 100755
index 0000000..bcca00b
--- /dev/null
+++ b/tt_user.h
@@ -0,0 +1,10 @@
+
+/* tt_user.h */
+
+
+void tt_user_init (struct tt_config_s *p);
+
+int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double latitude, 
+		double longitude, char *freq, char *comment, char mic_e, char *dao);
+
+void tt_user_background (void);
\ No newline at end of file
diff --git a/tune.h b/tune.h
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/tune.h
@@ -0,0 +1 @@
+ 
diff --git a/udp_test.c b/udp_test.c
new file mode 100755
index 0000000..68ca09e
--- /dev/null
+++ b/udp_test.c
@@ -0,0 +1,407 @@
+
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2012,2013  John Langner, WB2OSZ
+//    Contributed by Fabrice Faure for UDP part
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        udp_test.c
+ *
+ * Purpose:     Unit test for the udp reception with AFSK demodulator.
+ *
+ * Inputs:	Get data by listening on a given UDP port (first parameter is the udp port)
+ *
+ * Description:	This can be used to test the AFSK demodulator with udp data
+ *
+ *--------------------------------------------------------------------*/
+
+// #define X 1
+
+
+#include <stdio.h>
+#include <unistd.h>
+//#include <fcntl.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <time.h>
+
+#define UDPTEST_C 1
+
+#include "audio.h"
+#include "demod.h"
+// #include "fsk_demod_agc.h"
+#include "textcolor.h"
+#include "ax25_pad.h"
+#include "hdlc_rec2.h"
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <errno.h>
+static FILE *fp;
+static int e_o_f;
+static int packets_decoded = 0;
+//Bytes read in the current UDP socket buffer
+static int bytes_read = 0;
+//Total bytes read from UDP
+static int total_bytes_read = 0;
+//UDP socket used for receiving data
+static int sock;
+
+//UDP receiving port
+#define DEFAULT_UDP_PORT 6667
+//Maximum size of the UDP buffer (for allowing IP routing, udp packets are often limited to 1472 bytes)
+#define UDP_BUF_MAXLEN 20000
+//UDP receiving buffer , may use double or FIFO buffers in the future for better performance
+unsigned char udp_buf[UDP_BUF_MAXLEN];
+
+//TODO Provide cmdline parameters or config to change these values
+#define DEFAULT_UDP_NUM_CHANNELS 1
+#define DEFAULT_UDP_SAMPLES_PER_SEC 48000
+#define DEFAULT_UDP_BITS_PER_SAMPLE 16
+
+
+int main (int argc, char *argv[])
+{
+
+	struct audio_s modem;
+	int channel;
+	time_t start_time;
+	int udp_port;
+
+	text_color_init(1);
+	text_color_set(DW_COLOR_INFO);
+	if (argc < 2) {
+		udp_port = DEFAULT_UDP_PORT;
+		printf ("Using default UDP port : %d\n", udp_port);
+	} else {
+		udp_port = atoi(argv[1]);
+	}
+
+	struct sockaddr_in si_me;
+	int i, slen=sizeof(si_me);
+	int data_size = 0;
+
+	if ((sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
+		fprintf (stderr, "Couldn't create socket %d\n", errno);
+		exit(errno);
+	}
+
+	memset((char *) &si_me, 0, sizeof(si_me));
+	si_me.sin_family = AF_INET;
+	si_me.sin_port = htons(6667);
+	si_me.sin_addr.s_addr = htonl(INADDR_ANY);
+	if (bind(sock, &si_me, sizeof(si_me))==-1) {
+		fprintf (stderr, "Couldn't bind socket %d\n", errno);
+		exit(errno);
+	}
+	
+#ifdef DEBUG_RECEIVED_DATA
+	fp = fopen("udp.raw", "w");
+        if (fp == NULL) {
+           fprintf (stderr, "Couldn't open file for read: %s\n", argv[1]);
+	   //perror ("more info?");
+           exit (1);
+        }
+#endif
+
+	start_time = time(NULL);
+
+/* 
+ * First apply defaults.
+ * TODO: split config into two parts: _init (use here) and _read (only in direwolf).
+ */
+	modem.num_channels = DEFAULT_UDP_NUM_CHANNELS;
+	modem.samples_per_sec = DEFAULT_UDP_SAMPLES_PER_SEC;	
+	modem.bits_per_sample = DEFAULT_UDP_BITS_PER_SAMPLE;	
+
+	/* TODO: should have a command line option for this. */
+	//modem.fix_bits = RETRY_NONE;
+	//modem.fix_bits = RETRY_SINGLE;
+	//modem.fix_bits = RETRY_DOUBLE;
+	//modem.fix_bits = RETRY_TRIPLE;
+	modem.fix_bits = RETRY_TWO_SEP;
+	//Only one channel for UDP
+	channel = 0;
+
+	modem.modem_type[channel] = AFSK;		
+	modem.mark_freq[channel] = DEFAULT_MARK_FREQ;		
+	modem.space_freq[channel] = DEFAULT_SPACE_FREQ;		
+	modem.baud[channel] = DEFAULT_BAUD;				
+
+	strcpy (modem.profiles[channel], "C");	
+	// temp	
+	// strcpy (modem.profiles[channel], "F");		
+	modem.num_subchan[channel] = strlen(modem.profiles);	
+
+	//TODO: add -h command line option.
+//#define HF 1
+
+#if HF		
+	modem.mark_freq[channel] = 1600;		
+	modem.space_freq[channel] = 1800;		
+	modem.baud[channel] = 300;	
+	strcpy (modem.profiles[channel], "B"); 	
+	modem.num_subchan[channel] = strlen(modem.profiles);	
+#endif
+
+
+	modem.num_freq[channel] = 1;				
+	modem.offset[channel] = 0;				
+
+
+	text_color_set(DW_COLOR_INFO);
+	printf ("%d samples per second\n", modem.samples_per_sec);
+	printf ("%d bits per sample\n", modem.bits_per_sample);
+	printf ("%d audio channels\n", modem.num_channels);
+/*
+ * Initialize the AFSK demodulator and HDLC decoder.
+ */
+	multi_modem_init (&modem);
+
+
+	e_o_f = 0;
+	bytes_read = 0;
+	data_size = 0;	
+
+	while ( ! e_o_f ) 
+	{
+
+		int audio_sample;
+		int c;
+
+		//If all the data in the udp buffer has been processed, get new data from udp socket
+		if (bytes_read == data_size) {
+			data_size = buffer_get(UDP_BUF_MAXLEN);
+			//Got EOF packet 
+			if (data_size >= 0 && data_size <= 1) {
+				printf("Got NULL packet : terminate decoding (packet received with size %d)", data_size);
+				e_o_f = 1;
+				break;
+			}
+
+			bytes_read = 0; 
+		} 
+
+
+		/* This reads either 1 or 2 bytes depending on */
+		/* bits per sample.  */
+
+		audio_sample = demod_get_sample ();
+
+		if (audio_sample >= 256 * 256) 
+			e_o_f = 1;
+		multi_modem_process_sample(c,audio_sample);
+
+		/* When a complete frame is accumulated, */
+		/* process_rec_frame, below, is called. */
+
+	}
+
+	text_color_set(DW_COLOR_INFO);
+	printf ("\n\n");
+	printf ("%d packets decoded in %d seconds.\n", packets_decoded, (int)(time(NULL) - start_time));
+#ifdef DEBUG_RECEIVED_DATA
+	fclose(fp);
+#endif
+	exit (0);
+}
+
+int buffer_get (unsigned int size) {
+        struct sockaddr_in si_other;
+        int slen=sizeof(si_other);
+	int ch, res,i;
+	if (size > UDP_BUF_MAXLEN) { 
+		printf("size too big %d", size);
+		return -1;
+	}
+
+	res = recvfrom(sock, udp_buf, size, 0, &si_other, &slen);
+#ifdef DEBUG_RECEIVED_DATA
+	fwrite(udp_buf,res,1,fp);
+#endif
+
+	return res;
+}
+/*
+ * Simulate sample from the audio device.
+ */
+int audio_get (void)
+{
+	int ch;
+	ch = udp_buf[bytes_read];
+	bytes_read++;
+	total_bytes_read++;
+
+	return (ch);
+}
+
+
+
+/*
+ * Rather than queuing up frames with bad FCS, 
+ * try to fix them immediately.
+ */
+
+void rdq_append (rrbb_t rrbb)
+{
+	int chan;
+	int alevel;
+	int subchan;
+
+
+	chan = rrbb_get_chan(rrbb);
+	subchan = rrbb_get_subchan(rrbb);
+	alevel = rrbb_get_audio_level(rrbb);
+
+	hdlc_rec2_try_to_fix_later (rrbb, chan, subchan, alevel);
+}
+
+
+/*
+ * This is called when we have a good frame.
+ */
+
+void app_process_rec_packet (int chan, int subchan, packet_t pp, int alevel, retry_t retries, char *spectrum)  
+{	
+	
+	//int err;
+	//char *p;
+	char stemp[500];
+	unsigned char *pinfo;
+	int info_len;
+	int h;
+	char heard[20];
+	//packet_t pp;
+
+
+	packets_decoded++;
+
+
+	ax25_format_addrs (pp, stemp);
+
+	info_len = ax25_get_info (pp, &pinfo);
+
+	/* Print so we can see what is going on. */
+
+#if 1
+	/* Display audio input level. */
+        /* Who are we hearing?   Original station or digipeater. */
+
+	h = ax25_get_heard(pp);
+        ax25_get_addr_with_ssid(pp, h, heard);
+
+	text_color_set(DW_COLOR_DEBUG);
+	printf ("\n");
+
+	if (h != AX25_SOURCE) {
+	  printf ("Digipeater ");
+	}
+	printf ("%s audio level = %d   [%s]   %s\n", heard, alevel, retry_text[(int)retries], spectrum);
+
+
+#endif
+
+// Display non-APRS packets in a different color.
+
+	if (ax25_is_aprs(pp)) {
+	  text_color_set(DW_COLOR_REC);
+	  printf ("[%d] ", chan);
+	}
+	else {
+	  text_color_set(DW_COLOR_DEBUG);
+	  printf ("[%d] ", chan);
+	}
+
+	printf ("%s", stemp);			/* stations followed by : */
+	ax25_safe_print ((char *)pinfo, info_len, 0);
+	printf ("\n");
+
+	ax25_delete (pp);
+
+} /* end app_process_rec_packet */
+
+
+
+
+
+
+/* Current time in seconds but more resolution than time(). */
+
+/* We don't care what date a 0 value represents because we */
+/* only use this to calculate elapsed time. */
+
+
+
+double dtime_now (void)
+{
+#if __WIN32__
+	/* 64 bit integer is number of 100 nanosecond intervals from Jan 1, 1601. */
+
+	FILETIME ft;
+	
+	GetSystemTimeAsFileTime (&ft);
+
+	return ((( (double)ft.dwHighDateTime * (256. * 256. * 256. * 256.) + 
+			(double)ft.dwLowDateTime ) / 10000000.) - 11644473600.);
+#else
+	/* tv_sec is seconds from Jan 1, 1970. */
+
+	struct timespec ts;
+	int sec, ns;
+	double x1, x2;
+	double result;
+
+	clock_gettime (CLOCK_REALTIME, &ts);
+
+	//result = (double)(ts.tv_sec) + (double)(ts.tv_nsec) / 1000000000.;
+	//result = (double)(ts.tv_sec) + ((double)(ts.tv_nsec) * .001 * .001 *.001);
+	sec = (int)(ts.tv_sec);
+	ns = (int)(ts.tv_nsec);
+	x1 = (double)(sec);
+	//x1 = (double)(sec-1300000000);	/* try to work around strange result. */
+	//x2 = (double)(ns) * .001 * .001 *.001;
+	x2 = (double)(ns/1000000) *.001;
+	result = x1 + x2;
+
+	/* Sometimes this returns NAN.  How could that possibly happen? */
+	/* This is REALLY BIZARRE! */
+	/* Multiplying a number by a billionth often produces NAN. */
+	/* Adding a fraction to a number over a billion often produces NAN. */
+	
+	/* Hardware problem???  Need to test on different computer. */
+
+	if (isnan(result)) {
+	  text_color_set(DW_COLOR_ERROR);
+	  printf ("\ndtime_now(): %d, %d -> %.3f + %.3f -> NAN!!!\n\n", sec, ns, x1, x2);
+	}
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	printf ("dtime_now() returns %.3f\n", result);
+#endif
+
+	return (result);
+#endif
+}
+
+
+
+/* end atest.c */
diff --git a/utm/LatLong-UTMconversion.c b/utm/LatLong-UTMconversion.c
new file mode 100755
index 0000000..609f400
--- /dev/null
+++ b/utm/LatLong-UTMconversion.c
@@ -0,0 +1,190 @@
+//LatLong- UTM conversion.c
+//Lat Long - UTM, UTM - Lat Long conversions
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "constants.h"
+#include "LatLong-UTMconversion.h"
+
+
+/*Reference ellipsoids derived from Peter H. Dana's website- 
+http://www.utexas.edu/depts/grg/gcraft/notes/datum/elist.html
+Department of Geography, University of Texas at Austin
+Internet: pdana at mail.utexas.edu
+3/22/95
+
+Source
+Defense Mapping Agency. 1987b. DMA Technical Report: Supplement to Department of Defense World Geodetic System
+1984 Technical Report. Part I and II. Washington, DC: Defense Mapping Agency
+*/
+
+
+
+void LLtoUTM(int ReferenceEllipsoid, const double Lat, const double Long, 
+			 double *UTMNorthing, double *UTMEasting, char* UTMZone)
+{
+//converts lat/long to UTM coords.  Equations from USGS Bulletin 1532 
+//East Longitudes are positive, West longitudes are negative. 
+//North latitudes are positive, South latitudes are negative
+//Lat and Long are in decimal degrees
+	//Written by Chuck Gantz- chuck.gantz at globalstar.com
+
+	double a = ellipsoid[ReferenceEllipsoid].EquatorialRadius;
+	double eccSquared = ellipsoid[ReferenceEllipsoid].eccentricitySquared;
+	double k0 = 0.9996;
+
+	double LongOrigin;
+	double eccPrimeSquared;
+	double N, T, C, A, M;
+	
+//Make sure the longitude is between -180.00 .. 179.9
+	double LongTemp = (Long+180)-(int)((Long+180)/360)*360-180; // -180.00 .. 179.9;
+
+	double LatRad = Lat*deg2rad;
+	double LongRad = LongTemp*deg2rad;
+	double LongOriginRad;
+	int    ZoneNumber;
+
+	ZoneNumber = (int)((LongTemp + 180)/6) + 1;
+  
+	if( Lat >= 56.0 && Lat < 64.0 && LongTemp >= 3.0 && LongTemp < 12.0 )
+		ZoneNumber = 32;
+
+  // Special zones for Svalbard
+	if( Lat >= 72.0 && Lat < 84.0 ) 
+	{
+	  if(      LongTemp >= 0.0  && LongTemp <  9.0 ) ZoneNumber = 31;
+	  else if( LongTemp >= 9.0  && LongTemp < 21.0 ) ZoneNumber = 33;
+	  else if( LongTemp >= 21.0 && LongTemp < 33.0 ) ZoneNumber = 35;
+	  else if( LongTemp >= 33.0 && LongTemp < 42.0 ) ZoneNumber = 37;
+	 }
+	LongOrigin = (ZoneNumber - 1)*6 - 180 + 3;  //+3 puts origin in middle of zone
+	LongOriginRad = LongOrigin * deg2rad;
+
+	//compute the UTM Zone from the latitude and longitude
+	sprintf(UTMZone, "%d%c", ZoneNumber, UTMLetterDesignator(Lat));
+
+	eccPrimeSquared = (eccSquared)/(1-eccSquared);
+
+	N = a/sqrt(1-eccSquared*sin(LatRad)*sin(LatRad));
+	T = tan(LatRad)*tan(LatRad);
+	C = eccPrimeSquared*cos(LatRad)*cos(LatRad);
+	A = cos(LatRad)*(LongRad-LongOriginRad);
+
+	M = a*((1	- eccSquared/4		- 3*eccSquared*eccSquared/64	- 5*eccSquared*eccSquared*eccSquared/256)*LatRad 
+				- (3*eccSquared/8	+ 3*eccSquared*eccSquared/32	+ 45*eccSquared*eccSquared*eccSquared/1024)*sin(2*LatRad)
+									+ (15*eccSquared*eccSquared/256 + 45*eccSquared*eccSquared*eccSquared/1024)*sin(4*LatRad) 
+									- (35*eccSquared*eccSquared*eccSquared/3072)*sin(6*LatRad));
+	
+	*UTMEasting = (double)(k0*N*(A+(1-T+C)*A*A*A/6
+					+ (5-18*T+T*T+72*C-58*eccPrimeSquared)*A*A*A*A*A/120)
+					+ 500000.0);
+
+	*UTMNorthing = (double)(k0*(M+N*tan(LatRad)*(A*A/2+(5-T+9*C+4*C*C)*A*A*A*A/24
+				 + (61-58*T+T*T+600*C-330*eccPrimeSquared)*A*A*A*A*A*A/720)));
+	if(Lat < 0)
+		*UTMNorthing += 10000000.0; //10000000 meter offset for southern hemisphere
+}
+
+char UTMLetterDesignator(double Lat)
+{
+//This routine determines the correct UTM letter designator for the given latitude
+//returns 'Z' if latitude is outside the UTM limits of 84N to 80S
+	//Written by Chuck Gantz- chuck.gantz at globalstar.com
+	char LetterDesignator;
+
+	if((84 >= Lat) && (Lat >= 72)) LetterDesignator = 'X';
+	else if((72 > Lat) && (Lat >= 64)) LetterDesignator = 'W';
+	else if((64 > Lat) && (Lat >= 56)) LetterDesignator = 'V';
+	else if((56 > Lat) && (Lat >= 48)) LetterDesignator = 'U';
+	else if((48 > Lat) && (Lat >= 40)) LetterDesignator = 'T';
+	else if((40 > Lat) && (Lat >= 32)) LetterDesignator = 'S';
+	else if((32 > Lat) && (Lat >= 24)) LetterDesignator = 'R';
+	else if((24 > Lat) && (Lat >= 16)) LetterDesignator = 'Q';
+	else if((16 > Lat) && (Lat >= 8)) LetterDesignator = 'P';
+	else if(( 8 > Lat) && (Lat >= 0)) LetterDesignator = 'N';
+	else if(( 0 > Lat) && (Lat >= -8)) LetterDesignator = 'M';
+	else if((-8> Lat) && (Lat >= -16)) LetterDesignator = 'L';
+	else if((-16 > Lat) && (Lat >= -24)) LetterDesignator = 'K';
+	else if((-24 > Lat) && (Lat >= -32)) LetterDesignator = 'J';
+	else if((-32 > Lat) && (Lat >= -40)) LetterDesignator = 'H';
+	else if((-40 > Lat) && (Lat >= -48)) LetterDesignator = 'G';
+	else if((-48 > Lat) && (Lat >= -56)) LetterDesignator = 'F';
+	else if((-56 > Lat) && (Lat >= -64)) LetterDesignator = 'E';
+	else if((-64 > Lat) && (Lat >= -72)) LetterDesignator = 'D';
+	else if((-72 > Lat) && (Lat >= -80)) LetterDesignator = 'C';
+	else LetterDesignator = 'Z'; //This is here as an error flag to show that the Latitude is outside the UTM limits
+
+	return LetterDesignator;
+}
+
+
+void UTMtoLL(int ReferenceEllipsoid, const double UTMNorthing, const double UTMEasting, const char* UTMZone,
+			  double *Lat,  double *Long )
+{
+//converts UTM coords to lat/long.  Equations from USGS Bulletin 1532 
+//East Longitudes are positive, West longitudes are negative. 
+//North latitudes are positive, South latitudes are negative
+//Lat and Long are in decimal degrees. 
+	//Written by Chuck Gantz- chuck.gantz at globalstar.com
+
+	double k0 = 0.9996;
+	double a = ellipsoid[ReferenceEllipsoid].EquatorialRadius;
+	double eccSquared = ellipsoid[ReferenceEllipsoid].eccentricitySquared;
+	double eccPrimeSquared;
+	double e1 = (1-sqrt(1-eccSquared))/(1+sqrt(1-eccSquared));
+	double N1, T1, C1, R1, D, M;
+	double LongOrigin;
+	double mu, phi1, phi1Rad;
+	double x, y;
+	int ZoneNumber;
+	char* ZoneLetter;
+	int NorthernHemisphere; //1 for northern hemispher, 0 for southern
+
+	x = UTMEasting - 500000.0; //remove 500,000 meter offset for longitude
+	y = UTMNorthing;
+
+	ZoneNumber = strtoul(UTMZone, &ZoneLetter, 10);
+	if (*ZoneLetter == '\0') 
+	{
+		NorthernHemisphere = 1;  //no letter - assume northern hemisphere
+	}
+	else if((*ZoneLetter >= 'N' && *ZoneLetter <= 'X') ||
+	        (*ZoneLetter >= 'n' && *ZoneLetter <= 'x'))
+	{
+		NorthernHemisphere = 1;  //point is in northern hemisphere
+	}
+	else
+	{
+		NorthernHemisphere = 0;  //point is in southern hemisphere
+		y -= 10000000.0;  //remove 10,000,000 meter offset used for southern hemisphere
+	}
+
+	LongOrigin = (ZoneNumber - 1)*6 - 180 + 3;  //+3 puts origin in middle of zone
+
+	eccPrimeSquared = (eccSquared)/(1-eccSquared);
+
+	M = y / k0;
+	mu = M/(a*(1-eccSquared/4-3*eccSquared*eccSquared/64-5*eccSquared*eccSquared*eccSquared/256));
+
+	phi1Rad = mu	+ (3*e1/2-27*e1*e1*e1/32)*sin(2*mu) 
+				+ (21*e1*e1/16-55*e1*e1*e1*e1/32)*sin(4*mu)
+				+(151*e1*e1*e1/96)*sin(6*mu);
+	phi1 = phi1Rad*rad2deg;
+
+	N1 = a/sqrt(1-eccSquared*sin(phi1Rad)*sin(phi1Rad));
+	T1 = tan(phi1Rad)*tan(phi1Rad);
+	C1 = eccPrimeSquared*cos(phi1Rad)*cos(phi1Rad);
+	R1 = a*(1-eccSquared)/pow(1-eccSquared*sin(phi1Rad)*sin(phi1Rad), 1.5);
+	D = x/(N1*k0);
+
+	*Lat = phi1Rad - (N1*tan(phi1Rad)/R1)*(D*D/2-(5+3*T1+10*C1-4*C1*C1-9*eccPrimeSquared)*D*D*D*D/24
+					+(61+90*T1+298*C1+45*T1*T1-252*eccPrimeSquared-3*C1*C1)*D*D*D*D*D*D/720);
+	*Lat = *Lat * rad2deg;
+
+	*Long = (D-(1+2*T1+C1)*D*D*D/6+(5-2*C1+28*T1-3*C1*C1+8*eccPrimeSquared+24*T1*T1)
+					*D*D*D*D*D/120)/cos(phi1Rad);
+	*Long = LongOrigin + *Long * rad2deg;
+
+}
diff --git a/utm/LatLong-UTMconversion.h b/utm/LatLong-UTMconversion.h
new file mode 100755
index 0000000..8c0fbbb
--- /dev/null
+++ b/utm/LatLong-UTMconversion.h
@@ -0,0 +1,30 @@
+//LatLong- UTM conversion..h
+//definitions for lat/long to UTM and UTM to lat/lng conversions
+#include <string.h>
+
+#ifndef LATLONGCONV
+#define LATLONGCONV
+
+void LLtoUTM(int ReferenceEllipsoid, const double Lat, const double Long, 
+			 double *UTMNorthing, double *UTMEasting, char* UTMZone);
+void UTMtoLL(int ReferenceEllipsoid, const double UTMNorthing, const double UTMEasting, const char* UTMZone,
+			  double *Lat,  double *Long );
+char UTMLetterDesignator(double Lat);
+void LLtoSwissGrid(const double Lat, const double Long, 
+			 double *SwissNorthing, double *SwissEasting);
+void SwissGridtoLL(const double SwissNorthing, const double SwissEasting, 
+					double *Lat, double *Long);
+
+struct Ellipsoid_s {
+	int id;
+	char* ellipsoidName;
+	double EquatorialRadius; 
+	double eccentricitySquared;  
+};
+
+typedef struct Ellipsoid_s Ellipsoid;
+
+#define WSG84 23
+
+
+#endif
diff --git a/utm/README.txt b/utm/README.txt
new file mode 100755
index 0000000..0f7a041
--- /dev/null
+++ b/utm/README.txt
@@ -0,0 +1,10 @@
+
+Most of the files in this directory copied from
+
+http://www.gpsy.com/gpsinfo/geotoutm/
+
+
+A few minor modifications were made:
+
+1. Convert from C++ to C.
+2. Make the zone check more robust.
\ No newline at end of file
diff --git a/utm/SwissGrid.cpp b/utm/SwissGrid.cpp
new file mode 100755
index 0000000..3bc72fa
--- /dev/null
+++ b/utm/SwissGrid.cpp
@@ -0,0 +1,140 @@
+
+#include <math.h>
+#include "constants.h"
+#include "LatLong- UTM conversion.h"
+
+//forward declarations
+double CorrRatio(double LatRad, const double C);
+double NewtonRaphson(const double initEstimate);
+
+
+void LLtoSwissGrid(const double Lat, const double Long, 
+			 double &SwissNorthing, double &SwissEasting)
+{
+//converts lat/long to Swiss Grid coords.  Equations from "Supplementary PROJ.4 Notes-
+//Swiss Oblique Mercator Projection", August 5, 1995, Release 4.3.3, by Gerald I. Evenden
+//Lat and Long are in decimal degrees
+//This transformation is, of course, only valid in Switzerland
+	//Written by Chuck Gantz- chuck.gantz at globalstar.com
+	double a = ellipsoid[3].EquatorialRadius; //Bessel ellipsoid
+	double eccSquared = ellipsoid[3].eccentricitySquared;
+	double ecc = sqrt(eccSquared);
+
+	double LongOrigin = 7.43958333; //E7d26'22.500"
+	double LatOrigin = 46.95240556; //N46d57'8.660"
+
+	double LatRad = Lat*deg2rad;
+	double LongRad = Long*deg2rad;
+	double LatOriginRad = LatOrigin*deg2rad;
+	double LongOriginRad = LongOrigin*deg2rad;
+
+	double c = sqrt(1+((eccSquared * pow(cos(LatOriginRad), 4)) / (1-eccSquared))); 
+	
+	double equivLatOrgRadPrime = asin(sin(LatOriginRad) / c);
+
+	//eqn. 1
+	double K = log(tan(FOURTHPI + equivLatOrgRadPrime/2))
+				-c*(log(tan(FOURTHPI + LatOriginRad/2)) 
+					- ecc/2 * log((1+ecc*sin(LatOriginRad)) / (1-ecc*sin(LatOriginRad))));
+
+	
+	double LongRadPrime = c*(LongRad - LongOriginRad); //eqn 2
+	double w = c*(log(tan(FOURTHPI + LatRad/2)) 
+				- ecc/2 * log((1+ecc*sin(LatRad)) / (1-ecc*sin(LatRad)))) + K; //eqn 1
+	double LatRadPrime = 2 * (atan(exp(w)) - FOURTHPI); //eqn 1
+	
+	//eqn 3
+	double sinLatDoublePrime = cos(equivLatOrgRadPrime) * sin(LatRadPrime) 
+								- sin(equivLatOrgRadPrime) * cos(LatRadPrime) * cos(LongRadPrime);
+	double LatRadDoublePrime = asin(sinLatDoublePrime);
+	
+	//eqn 4
+	double sinLongDoublePrime = cos(LatRadPrime)*sin(LongRadPrime) / cos(LatRadDoublePrime);
+	double LongRadDoublePrime = asin(sinLongDoublePrime);
+
+	double R = a*sqrt(1-eccSquared) / (1-eccSquared*sin(LatOriginRad) * sin(LatOriginRad));
+
+	SwissNorthing = R*log(tan(FOURTHPI + LatRadDoublePrime/2)) + 200000.0; //eqn 5
+	SwissEasting = R*LongRadDoublePrime + 600000.0; //eqn 6
+
+}
+
+
+void SwissGridtoLL(const double SwissNorthing, const double SwissEasting, 
+					double& Lat, double& Long)
+{
+	double a = ellipsoid[3].EquatorialRadius; //Bessel ellipsoid
+	double eccSquared = ellipsoid[3].eccentricitySquared;
+	double ecc = sqrt(eccSquared);
+
+	double LongOrigin = 7.43958333; //E7d26'22.500"
+	double LatOrigin = 46.95240556; //N46d57'8.660"
+
+	double LatOriginRad = LatOrigin*deg2rad;
+	double LongOriginRad = LongOrigin*deg2rad;
+
+	double R = a*sqrt(1-eccSquared) / (1-eccSquared*sin(LatOriginRad) * sin(LatOriginRad));
+
+	double LatRadDoublePrime = 2*(atan(exp((SwissNorthing - 200000.0)/R)) - FOURTHPI); //eqn. 7
+	double LongRadDoublePrime = (SwissEasting - 600000.0)/R; //eqn. 8 with equation corrected
+
+
+	double c = sqrt(1+((eccSquared * pow(cos(LatOriginRad), 4)) / (1-eccSquared))); 
+	double equivLatOrgRadPrime = asin(sin(LatOriginRad) / c);
+
+	double sinLatRadPrime = cos(equivLatOrgRadPrime)*sin(LatRadDoublePrime)
+							+ sin(equivLatOrgRadPrime)*cos(LatRadDoublePrime)*cos(LongRadDoublePrime);
+	double LatRadPrime = asin(sinLatRadPrime);
+
+	double sinLongRadPrime = cos(LatRadDoublePrime)*sin(LongRadDoublePrime)/cos(LatRadPrime);
+	double LongRadPrime = asin(sinLongRadPrime);
+
+	Long = (LongRadPrime/c + LongOriginRad) * rad2deg;
+
+	Lat = NewtonRaphson(LatRadPrime) * rad2deg;
+
+}
+
+double NewtonRaphson(const double initEstimate)
+{
+	double Estimate = initEstimate;
+	double tol = 0.00001;
+	double corr;
+
+	double eccSquared = ellipsoid[3].eccentricitySquared;
+	double ecc = sqrt(eccSquared);
+
+	double LatOrigin = 46.95240556; //N46d57'8.660"
+	double LatOriginRad = LatOrigin*deg2rad;
+
+	double c = sqrt(1+((eccSquared * pow(cos(LatOriginRad), 4)) / (1-eccSquared))); 
+	
+	double equivLatOrgRadPrime = asin(sin(LatOriginRad) / c);
+
+	//eqn. 1
+	double K = log(tan(FOURTHPI + equivLatOrgRadPrime/2))
+				-c*(log(tan(FOURTHPI + LatOriginRad/2)) 
+					- ecc/2 * log((1+ecc*sin(LatOriginRad)) / (1-ecc*sin(LatOriginRad))));
+	double C = (K - log(tan(FOURTHPI + initEstimate/2)))/c;
+
+	do
+	{
+		corr = CorrRatio(Estimate, C);
+		Estimate = Estimate - corr;
+	}
+	while (fabs(corr) > tol);
+
+	return Estimate;
+}
+
+
+
+double CorrRatio(double LatRad, const double C)
+{
+	double eccSquared = ellipsoid[3].eccentricitySquared;
+	double ecc = sqrt(eccSquared);
+	double corr = (C + log(tan(FOURTHPI + LatRad/2)) 
+				- ecc/2 * log((1+ecc*sin(LatRad)) / (1-ecc*sin(LatRad)))) * (((1-eccSquared*sin(LatRad)*sin(LatRad)) * cos(LatRad)) / (1-eccSquared));
+
+	return corr;
+}
diff --git a/utm/UTMConversions.cpp b/utm/UTMConversions.cpp
new file mode 100755
index 0000000..1230e4f
--- /dev/null
+++ b/utm/UTMConversions.cpp
@@ -0,0 +1,39 @@
+//UTM Conversion.cpp- test program for lat/long to UTM and UTM to lat/long conversions
+#include <iostream.h>
+#include <iomanip.h>
+#include "LatLong-UTMconversion.h"
+
+
+void main()
+{
+	double Lat = 47.37816667;
+	double Long = 8.23250000;
+	double UTMNorthing;
+	double UTMEasting;
+	double SwissNorthing;
+	double SwissEasting;
+	char UTMZone[4];
+	int RefEllipsoid = 23;//WGS-84. See list with file "LatLong- UTM conversion.cpp" for id numbers
+
+	cout << "Starting position(Lat, Long):  " << Lat << "   " << Long <<endl;
+
+	LLtoUTM(RefEllipsoid, Lat, Long, UTMNorthing, UTMEasting, UTMZone);
+	cout << setiosflags(ios::showpoint | ios::fixed) << setprecision(5);
+	cout << "Calculated UTM position(Northing, Easting, Zone):  ";
+	cout << UTMNorthing << "   " << UTMEasting;
+	cout << "   " << UTMZone <<endl;
+	
+	UTMtoLL(RefEllipsoid, UTMNorthing, UTMEasting, UTMZone, Lat, Long);
+	cout << "Calculated Lat, Long position(Lat, Long):  " << Lat << "   " << Long << endl <<endl;
+
+	LLtoSwissGrid(Lat, Long, SwissNorthing, SwissEasting);
+	cout << setiosflags(ios::showpoint | ios::fixed) << setprecision(5);
+	cout << "Calculated Swiss Grid position(Northing, Easting):  ";
+	cout << SwissNorthing << "   " << SwissEasting << endl;
+}
+
+
+/* N 47.38195� E 8.54879�  (Swiss Grid: 683.748 248.342)
+  N 47�12.625' / E 7� 27.103'= N 47.21041667 E 7.45171667(Swiss Grid = 600920/228685) 
+  N 47�22.690' / E 8� 13.950'= N 47.37816667 E 8.23250000 (Swiss Grid = 659879/247637)
+*/
diff --git a/utm/constants.h b/utm/constants.h
new file mode 100755
index 0000000..35737e5
--- /dev/null
+++ b/utm/constants.h
@@ -0,0 +1,41 @@
+//constants.h
+
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+#include "LatLong-UTMconversion.h"
+
+#define PI M_PI
+#define FOURTHPI (PI / 4)
+#define deg2rad (PI / 180)
+#define rad2deg (180.0 / PI)
+
+static const Ellipsoid ellipsoid[] = 
+{//  id, Ellipsoid name, Equatorial Radius, square of eccentricity	
+	{ -1, "Placeholder", 0, 0 },//placeholder only, To allow array indices to match id numbers
+	{ 1, "Airy", 6377563, 0.00667054 },
+	{ 2, "Australian National", 6378160, 0.006694542 },
+	{ 3, "Bessel 1841", 6377397, 0.006674372 },
+	{ 4, "Bessel 1841 (Nambia) ", 6377484, 0.006674372 },
+	{ 5, "Clarke 1866", 6378206, 0.006768658 },
+	{ 6, "Clarke 1880", 6378249, 0.006803511 },
+	{ 7, "Everest", 6377276, 0.006637847 },
+	{ 8, "Fischer 1960 (Mercury) ", 6378166, 0.006693422 },
+	{ 9, "Fischer 1968", 6378150, 0.006693422 },
+	{ 10, "GRS 1967", 6378160, 0.006694605 },
+	{ 11, "GRS 1980", 6378137, 0.00669438 },
+	{ 12, "Helmert 1906", 6378200, 0.006693422 },
+	{ 13, "Hough", 6378270, 0.00672267 },
+	{ 14, "International", 6378388, 0.00672267 },
+	{ 15, "Krassovsky", 6378245, 0.006693422 },
+	{ 16, "Modified Airy", 6377340, 0.00667054 },
+	{ 17, "Modified Everest", 6377304, 0.006637847 },
+	{ 18, "Modified Fischer 1960", 6378155, 0.006693422 },
+	{ 19, "South American 1969", 6378160, 0.006694542 },
+	{ 20, "WGS 60", 6378165, 0.006693422 },
+	{ 21, "WGS 66", 6378145, 0.006694542 },
+	{ 22, "WGS-72", 6378135, 0.006694318 },
+	{ 23, "WGS-84", 6378137, 0.00669438 }
+};
+
+#endif
diff --git a/utm2ll.c b/utm2ll.c
new file mode 100755
index 0000000..7e68c7c
--- /dev/null
+++ b/utm2ll.c
@@ -0,0 +1,72 @@
+/* UTM to Latitude / Longitude conversion */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "LatLong-UTMconversion.h"
+
+
+static void usage();
+
+
+void main (int argc, char *argv[]) 
+{
+	double easting;
+	double northing;
+	double lat, lon;
+	char zone[100];
+	int znum;
+	char *zlet;
+
+	if (argc != 4) usage();
+
+	strcpy (zone, argv[1]);
+	znum = strtoul(zone, &zlet, 10);
+
+	if (znum < 1 || znum > 60) {
+	  fprintf (stderr, "Zone number is out of range.\n\n");
+	  usage();
+	}
+
+	//printf ("zlet = %c 0x%02x\n", *zlet, *zlet);
+	if (*zlet != '\0' && strchr ("CDEFGHJKLMNPQRSTUVWX", *zlet) == NULL) {
+	  fprintf (stderr, "Latitudinal band must be one of CDEFGHJKLMNPQRSTUVWX.\n\n");
+	  usage();
+	}
+
+	easting = atof(argv[2]);
+	if (easting < 0 || easting > 999999) {
+	  fprintf (stderr, "Easting value is out of range.\n\n");
+	  usage();
+	}
+
+	northing = atof(argv[3]);
+	if (northing < 0 || northing > 9999999) {
+	  fprintf (stderr, "Northing value is out of range.\n\n");
+	  usage();
+	}
+
+	UTMtoLL (WSG84, northing, easting, zone, &lat, &lon);
+
+	printf ("latitude = %f, longitude = %f\n", lat, lon);
+}
+
+
+static void usage (void)
+{
+	fprintf (stderr, "UTM to Latitude / Longitude conversion\n");
+	fprintf (stderr, "\n");
+	fprintf (stderr, "Usage:\n");
+	fprintf (stderr, "\tutm2ll  zone  easting  northing\n");
+	fprintf (stderr, "\n");
+	fprintf (stderr, "where,\n");
+	fprintf (stderr, "\tzone is UTM zone 1 thru 60 with optional latitudinal band.\n");
+	fprintf (stderr, "\teasting is x coordinate in meters\n");
+	fprintf (stderr, "\tnorthing is y coordinate in meters\n");
+	fprintf (stderr, "\n");
+	fprintf (stderr, "Example:\n");
+	fprintf (stderr, "\tutm2ll 19T 306130 4726010\n");
+
+	exit (1);
+}
\ No newline at end of file
diff --git a/version.h b/version.h
new file mode 100755
index 0000000..f9cc894
--- /dev/null
+++ b/version.h
@@ -0,0 +1,7 @@
+
+/* Dire Wolf version 1.0 */
+
+#define APP_TOCALL "APDW"
+
+#define MAJOR_VERSION 1
+#define MINOR_VERSION 0
diff --git a/xmit.c b/xmit.c
new file mode 100755
index 0000000..d4222e0
--- /dev/null
+++ b/xmit.c
@@ -0,0 +1,728 @@
+//
+//    This file is part of Dire Wolf, an amateur radio packet TNC.
+//
+//    Copyright (C) 2011,2013  John Langner, WB2OSZ
+//
+//    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, see <http://www.gnu.org/licenses/>.
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * Module:      xmit.c
+ *
+ * Purpose:   	Transmit queued up packets when channel is clear.
+ *		
+ * Description:	Producers of packets to be transmitted call tq_append and then
+ *		go merrily on their way, unconcerned about when the packet might
+ *		actually get transmitted.
+ *
+ *		This thread waits until the channel is clear and then removes
+ *		packets from the queue and transmits them.
+ *
+ *
+ * Usage:	(1) The main application calls xmit_init.
+ *
+ *			This will initialize the transmit packet queue
+ *			and create a thread to empty the queue when
+ *			the channel is clear.
+ *
+ *		(2) The application queues up packets by calling tq_append.
+ *
+ *			Packets that are being digipeated should go in the 
+ *			high priority queue so they will go out first.
+ *
+ *			Other packets should go into the lower priority queue.
+ *
+ *		(3) xmit_thread removes packets from the queue and transmits
+ *			them when other signals are not being heard.
+ *
+ *---------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+
+//#include <sys/time.h>
+#include <time.h>
+
+#if __WIN32__
+#include <windows.h>
+#endif
+
+#include "direwolf.h"
+#include "ax25_pad.h"
+#include "textcolor.h"
+#include "audio.h"
+#include "tq.h"
+#include "xmit.h"
+#include "hdlc_send.h"
+#include "hdlc_rec.h"
+#include "ptt.h"
+
+
+static int xmit_num_channels;		/* Number of radio channels. */
+
+
+/*
+ * Parameters for transmission.
+ * Each channel can have different timing values.
+ *
+ * These are initialized once at application startup time
+ * and some can be changed later by commands from connected applications.
+ */
+
+
+
+
+
+static int xmit_slottime[MAX_CHANS];	/* Slot time in 10 mS units for persistance algorithm. */
+
+static int xmit_persist[MAX_CHANS];	/* Sets probability for transmitting after each */
+					/* slot time delay.  Transmit if a random number */
+					/* in range of 0 - 255 <= persist value.  */
+					/* Otherwise wait another slot time and try again. */
+
+static int xmit_txdelay[MAX_CHANS];	/* After turning on the transmitter, */
+					/* send "flags" for txdelay * 10 mS. */
+
+static int xmit_txtail[MAX_CHANS];		/* Amount of time to keep transmitting after we */
+					/* are done sending the data.  This is to avoid */
+					/* dropping PTT too soon and chopping off the end */
+					/* of the frame.  Again 10 mS units. */
+
+static int xmit_bits_per_sec[MAX_CHANS];	/* Data transmission rate. */
+					/* Often called baud rate which is equivalent in */
+					/* this case but could be different with other */
+					/* modulation techniques. */
+
+
+#define BITS_TO_MS(b,ch) (((b)*1000)/xmit_bits_per_sec[(ch)])
+
+#define MS_TO_BITS(ms,ch) (((ms)*xmit_bits_per_sec[(ch)])/1000)
+
+
+
+static void * xmit_thread (void *arg);
+static int wait_for_clear_channel (int channel, int nowait, int slotttime, int persist);
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        xmit_init
+ *
+ * Purpose:     Initialize the transmit process.
+ *
+ * Inputs:	modem		- Structure with modem and timing parameters.
+ *
+ *
+ * Outputs:	Remember required information for future use.
+ *
+ * Description:	Initialize the queue to be empty and set up other
+ *		mechanisms for sharing it between different threads.
+ *
+ *		Start up xmit_thread to actually send the packets
+ *		at the appropriate time.
+ *
+ *--------------------------------------------------------------------*/
+
+
+
+void xmit_init (struct audio_s *p_modem)
+{
+	int j;
+#if __WIN32__
+	HANDLE xmit_th;
+#else
+	//pthread_attr_t attr;
+	//struct sched_param sp;
+	pthread_t xmit_tid;
+#endif
+	int e;
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("xmit_init ( ... )\n");
+#endif
+
+/*
+ * Push to Talk (PTT) control.
+ */
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("xmit_init: about to call ptt_init \n");
+#endif
+	ptt_init (p_modem);
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("xmit_init: back from ptt_init \n");
+#endif
+
+/* 
+ * Save parameters for later use.
+ */
+	xmit_num_channels = p_modem->num_channels;
+	assert (xmit_num_channels >= 1 && xmit_num_channels <= MAX_CHANS);
+
+	for (j=0; j<MAX_CHANS; j++) {
+	  xmit_bits_per_sec[j] = p_modem->baud[j];
+	  xmit_slottime[j] = p_modem->slottime[j];
+	  xmit_persist[j] = p_modem->persist[j];
+	  xmit_txdelay[j] = p_modem->txdelay[j];
+	  xmit_txtail[j] = p_modem->txtail[j];
+	}
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("xmit_init: about to call tq_init \n");
+#endif
+	tq_init (xmit_num_channels);
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("xmit_init: about to create thread \n");
+#endif
+
+//TODO:  xmit thread should be higher priority to avoid
+// underrun on the audio output device.
+
+#if __WIN32__
+	xmit_th = _beginthreadex (NULL, 0, xmit_thread, NULL, 0, NULL);
+	if (xmit_th == NULL) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("Could not create xmit thread\n");
+	  return;
+	}
+#else
+
+#if 0
+
+//TODO: not this simple.  probably need FIFO policy.
+	pthread_attr_init (&attr);
+  	e = pthread_attr_getschedparam (&attr, &sp);
+	if (e != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror("pthread_attr_getschedparam");
+	}
+
+	text_color_set(DW_COLOR_ERROR);
+	dw_printf ("Default scheduling priority = %d, min=%d, max=%d\n", 
+		sp.sched_priority, 
+		sched_get_priority_min(SCHED_OTHER),
+		sched_get_priority_max(SCHED_OTHER));
+	sp.sched_priority--;
+
+  	e = pthread_attr_setschedparam (&attr, &sp);
+	if (e != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror("pthread_attr_setschedparam");
+	}
+	
+	e = pthread_create (&xmit_tid, &attr, xmit_thread, (void *)0);
+	pthread_attr_destroy (&attr);
+#else
+	e = pthread_create (&xmit_tid, NULL, xmit_thread, (void *)0);
+#endif
+	if (e != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  perror("Could not create xmit thread");
+	  return;
+	}
+#endif
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("xmit_init: finished \n");
+#endif
+
+
+} /* end tq_init */
+
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        xmit_set_txdelay
+ *		xmit_set_persist
+ *		xmit_set_slottime
+ *		xmit_set_txtail
+ *				
+ *
+ * Purpose:     The KISS protocol, and maybe others, can specify
+ *		transmit timing parameters.  If the application
+ *		specifies these, they will override what was read
+ *		from the configuration file.
+ *
+ * Inputs:	channel	- should be 0 or 1.
+ *
+ *		value	- time values are in 10 mSec units.
+ *
+ *
+ * Outputs:	Remember required information for future use.
+ *
+ * Question:	Should we have an option to enable or disable the
+ *		application changing these values?
+ *
+ * Bugs:	No validity checking other than array subscript out of bounds.
+ *
+ *--------------------------------------------------------------------*/
+
+void xmit_set_txdelay (int channel, int value)
+{
+	if (channel >= 0 && channel < MAX_CHANS) {
+	  xmit_txdelay[channel] = value;
+	}
+}
+
+void xmit_set_persist (int channel, int value)
+{
+	if (channel >= 0 && channel < MAX_CHANS) {
+	  xmit_persist[channel] = value;
+	}
+}
+
+void xmit_set_slottime (int channel, int value)
+{
+	if (channel >= 0 && channel < MAX_CHANS) {
+	  xmit_slottime[channel] = value;
+	}
+}
+
+void xmit_set_txtail (int channel, int value)
+{
+	if (channel >= 0 && channel < MAX_CHANS) {
+	  xmit_txtail[channel] = value;
+	}
+}
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        xmit_thread
+ *
+ * Purpose:     Initialize the transmit process.
+ *
+ * Inputs:	None.
+ *
+ * Outputs:	
+ *
+ * Description:	Initialize the queue to be empty and set up other
+ *		mechanisms for sharing it between different threads.
+ *
+ *		We have different timing rules for different types of
+ *		packets so they are put into different queues.
+ *
+ *		High Priority -
+ *
+ *			Packets which are being digipeated go out first.
+ *			Latest recommendations are to retransmit these
+ *			immdediately (after no one else is heard, of course)
+ *			rather than waiting random times to avoid collisions.
+ *			The KPC-3 configuration option for this is "UIDWAIT OFF".  (?)
+ *
+ *		Low Priority - 
+ *
+ *			Other packets are sent after a random wait time
+ *			(determined by PERSIST & SLOTTIME) to help avoid
+ *			collisions.	
+ *
+ *		If more than one audio channel is being used, a separate
+ *		pair of transmit queues is used for each channel.
+ *
+ *
+ * Thought for future research:
+ *
+ *		Should we send multiple frames in one transmission if we 
+ *		have more than one sitting in the queue?  At first I was thinking
+ *		this would help reduce channel congestion.  I don't recall seeing
+ *		anything in the specifications allowing or disallowing multiple
+ *		frames in one transmission.  I can think of some scenarios 
+ *		where it might help.  I can think of some where it would 
+ *		definitely be counter productive.  
+ *		For now, one frame per transmission.
+ *
+ * What to others have to say about this topic?
+ *
+ *	"For what it is worth, the original APRSdos used a several second random
+ *	generator each time any kind of packet was generated... This is to avoid
+ *	bundling. Because bundling, though good for connected packet, is not good
+ *	on APRS. Sometimes the digi begins digipeating the first packet in the
+ *	bundle and steps all over the remainder of them. So best to make sure each
+ *	packet is isolated in time from others..."
+ *	
+ *		Bob, WB4APR
+ *	
+ *
+ * Version 0.9:	Earlier versions always sent one frame per transmission.
+ *		This was fine for APRS but more and more people are now
+ *		using this as a KISS TNC for connected protocols.
+ *		Rather than having a MAXFRAME configuration file item,
+ *		we try setting the maximum number automatically.
+ *		1 for digipeated frames, 7 for others.
+ *
+ *--------------------------------------------------------------------*/
+
+static void * xmit_thread (void *arg)
+{
+	packet_t pp;
+    	unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
+    	int flen;
+	int c, p;
+	char stemp[1024];	/* max size needed? */
+	int info_len;
+	unsigned char *pinfo;
+	int pre_flags, post_flags;
+	int num_bits;		/* Total number of bits in transmission */
+				/* including all flags and bit stuffing. */
+	int duration;		/* Transmission time in milliseconds. */
+	int already;
+	int wait_more;
+	int ok;
+
+	int maxframe;		/* Maximum number of frames for one transmission. */
+	int numframe;		/* Number of frames sent during this transmission. */
+
+/*
+ * These are for timing of a transmission.
+ * All are in usual unix time (seconds since 1/1/1970) but higher resolution
+ */
+	double time_ptt;	/* Time when PTT is turned on. */
+	double time_now;	/* Current time. */
+
+
+
+	while (1) {
+
+	  tq_wait_while_empty ();
+#if DEBUG
+	  text_color_set(DW_COLOR_DEBUG);
+	  dw_printf ("xmit_thread: woke up\n");
+#endif
+	  
+	  for (p=0; p<TQ_NUM_PRIO; p++) {
+
+	    for (c=0; c<xmit_num_channels; c++) {
+
+	      pp = tq_remove (c, p);
+#if DEBUG
+	      text_color_set(DW_COLOR_DEBUG);
+	      dw_printf ("xmit_thread: tq_remove(chan=%d, prio=%d) returned %p\n", c, p, pp);
+#endif
+	      if (pp != NULL) {
+
+		maxframe = (p == TQ_PRIO_0_HI) ? 1 : 7;
+/* 
+ * Wait for the channel to be clear.
+ * For the high priority queue, begin transmitting immediately.
+ * For the low priority queue, wait a random amount of time, in hopes
+ * of minimizing collisions.
+ */
+	        ok = wait_for_clear_channel (c, (p==TQ_PRIO_0_HI), xmit_slottime[c], xmit_persist[c]);
+
+	        if (ok) {
+
+/*
+ * Channel is clear.  
+ * Turn on transmitter.
+ * Start sending leading flag bytes.
+ */
+		  time_ptt = dtime_now ();
+		  ptt_set (c, 1);
+
+		  pre_flags = MS_TO_BITS(xmit_txdelay[c] * 10, c) / 8;
+		  num_bits =  hdlc_send_flags (c, pre_flags, 0);
+
+/*
+ * Print trasmitted packet.  Prefix by channel and priority.
+ */
+	          ax25_format_addrs (pp, stemp);
+	          info_len = ax25_get_info (pp, &pinfo);
+	          text_color_set(DW_COLOR_XMIT);
+	          dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
+	          dw_printf ("%s", stemp);			/* stations followed by : */
+	          ax25_safe_print ((char *)pinfo, info_len, 0);
+	          dw_printf ("\n");
+
+		  flen = ax25_pack (pp, fbuf);
+		  assert (flen <= sizeof(fbuf));
+/*
+ * Transmit the frame.
+ */		
+		  num_bits += hdlc_send_frame (c, fbuf, flen);
+		  numframe = 1;
+		  ax25_delete (pp);
+
+/*
+ * Additional packets if available and not exceeding max.
+ */
+
+		  while (numframe < maxframe && tq_count (c,p) > 0) {
+
+	            pp = tq_remove (c, p);
+#if DEBUG
+	            text_color_set(DW_COLOR_DEBUG);
+	            dw_printf ("xmit_thread: tq_remove(chan=%d, prio=%d) returned %p\n", c, p, pp);
+#endif
+	            ax25_format_addrs (pp, stemp);
+	            info_len = ax25_get_info (pp, &pinfo);
+	            text_color_set(DW_COLOR_XMIT);
+	            dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
+	            dw_printf ("%s", stemp);			/* stations followed by : */
+	            ax25_safe_print ((char *)pinfo, info_len, 0);
+	            dw_printf ("\n");
+
+		    flen = ax25_pack (pp, fbuf);
+		    assert (flen <= sizeof(fbuf));
+/*
+ * Transmit the frame.
+ */		
+		    num_bits += hdlc_send_frame (c, fbuf, flen);
+		    numframe++;
+		    ax25_delete (pp);
+		  }
+
+/* 
+ * Generous TXTAIL because we don't know exactly when the sound is done.
+ */
+
+		  post_flags = MS_TO_BITS(xmit_txtail[c] * 10, c) / 8;
+		  num_bits += hdlc_send_flags (c, post_flags, 1);
+
+
+/* 
+ * We don't know when the sound has actually been produced.
+ * hldc_send finishes before anything starts coming out of the speaker.
+ * It's all queued up somewhere.
+ *
+ * Calculate duration of entire frame in milliseconds.
+ *
+ * Subtract out elapsed time already since PTT was turned to determine
+ * how much longer to wait til we turn PTT off.
+ */
+		  duration = BITS_TO_MS(num_bits, c);
+		  time_now = dtime_now();
+		  already = (int) ((time_now - time_ptt) * 1000.);
+		  wait_more = duration - already;
+
+#if DEBUG
+	          text_color_set(DW_COLOR_DEBUG);
+	          dw_printf ("xmit_thread: maxframe = %d, numframe = %d\n", maxframe, numframe);
+#endif
+
+/* 
+ * Wait for all audio to be out before continuing.
+ * Provide a hint at delay required in case we don't have a 
+ * way to ask the hardware when all the sound has been pushed out.
+ */
+// TODO:  We have an issue if this is negative.  That means
+// we couldn't generate the data fast enough for the sound
+// system output and there probably gaps in the signal.
+
+		  audio_wait(wait_more);		
+
+/*
+ * Turn off transmitter.
+ */
+		
+		  ptt_set (c, 0);
+	        }
+	        else {
+/*
+ * Timeout waiting for clear channel.
+ * Discard the packet.
+ * Display with ERROR color rather than XMIT color.
+ */
+
+	          text_color_set(DW_COLOR_ERROR);
+		  dw_printf ("Waited too long for clear channel.  Discarding packet below.\n");
+
+	          ax25_format_addrs (pp, stemp);
+
+	          info_len = ax25_get_info (pp, &pinfo);
+
+	          text_color_set(DW_COLOR_INFO);
+	          dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
+
+	          dw_printf ("%s", stemp);			/* stations followed by : */
+	          ax25_safe_print ((char *)pinfo, info_len, 0);
+	          dw_printf ("\n");
+		  ax25_delete (pp);
+
+		}
+	      } /* for each channel */
+	    } /* for high priority then low priority */
+	  }
+	}
+
+} /* end xmit_thread */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name:        wait_for_clear_channel
+ *
+ * Purpose:     Wait for the radio channel to be clear and any
+ *		additional time for collision avoidance.
+ *
+ * Inputs:	channel	-	Radio channel number.
+ *
+ *		nowait	- 	Should be true for the high priority queue
+ *				(packets being digipeated).  This will 
+ *				allow transmission immediately when the 
+ *				channel is clear rather than waiting a 
+ *				random amount of time.
+ *
+ *		slottime - 	Amount of time to wait for each iteration
+ *				of the waiting algorithm.  10 mSec units.
+ *
+ *		persist -	Probability of transmitting 
+ *
+ * Returns:	1 for OK.  0 for timeout.
+ *
+ * Description:	
+ *
+ * Transmit delay algorithm:
+ *
+ *		Wait for channel to be clear.
+ *		Return if nowait is true.
+ *
+ *		Wait slottime * 10 milliseconds.
+ *		Generate an 8 bit random number in range of 0 - 255.
+ *		If random number <= persist value, return.
+ *		Otherwise repeat.
+ *
+ * Example:
+ *
+ *		For typical values of slottime=10 and persist=63,
+ *
+ *		Delay		Probability
+ *		-----		-----------
+ *		100		.25					= 25%
+ *		200		.75 * .25				= 19%
+ *		300		.75 * .75 * .25				= 14%
+ *		400		.75 * .75 * .75 * .25			= 11%
+ *		500		.75 * .75 * .75 * .75 * .25		= 8%
+ *		600		.75 * .75 * .75 * .75 * .75 * .25	= 6%
+ *		700		.75 * .75 * .75 * .75 * .75 * .75 * .25	= 4%
+ *		etc.		...
+ *
+ *--------------------------------------------------------------------*/
+
+/* Give up if we can't get a clear channel in a minute. */
+
+#define WAIT_TIMEOUT_MS (60 * 1000)	
+#define WAIT_CHECK_EVERY_MS 10
+
+static int wait_for_clear_channel (int channel, int nowait, int slottime, int persist)
+{
+	int r;
+	int n;
+
+	n = 0;
+	while (hdlc_rec_data_detect_any(channel)) {
+	  SLEEP_MS(WAIT_CHECK_EVERY_MS);
+	  n++;
+	  if (n > (WAIT_TIMEOUT_MS / WAIT_CHECK_EVERY_MS)) {
+	    return 0;
+	  }
+	}
+
+	if (nowait) {
+	  return 1;
+	}
+
+	while (1) {
+
+	  SLEEP_MS (slottime * 10);
+
+	  if (hdlc_rec_data_detect_any(channel)) {
+	    continue;   
+	  }
+
+	  r = rand() & 0xff;
+	  if (r <= persist) {
+	    return 1;
+ 	  }	
+	}
+
+} /* end wait_for_clear_channel */
+
+
+
+
+/* Current time in seconds but more resolution than time(). */
+
+/* We don't care what date a 0 value represents because we */
+/* only use this to calculate elapsed time. */
+
+
+
+double dtime_now (void)
+{
+#if __WIN32__
+	/* 64 bit integer is number of 100 nanosecond intervals from Jan 1, 1601. */
+
+	FILETIME ft;
+	
+	GetSystemTimeAsFileTime (&ft);
+
+	return ((( (double)ft.dwHighDateTime * (256. * 256. * 256. * 256.) + 
+			(double)ft.dwLowDateTime ) / 10000000.) - 11644473600.);
+#else
+	/* tv_sec is seconds from Jan 1, 1970. */
+
+	struct timespec ts;
+	int sec, ns;
+	double x1, x2;
+	double result;
+
+	clock_gettime (CLOCK_REALTIME, &ts);
+
+	sec = (int)(ts.tv_sec);
+	ns = (int)(ts.tv_nsec);
+	x1 = (double)(sec);
+	x2 = (double)(ns/1000000) *.001;
+	result = x1 + x2;
+
+	/* Sometimes this returns NAN.  How could that possibly happen? */
+	/* This is REALLY BIZARRE! */
+	/* Multiplying a number by a billionth often produces NAN. */
+	/* Adding a fraction to a number over a billion often produces NAN. */
+	
+	/* Turned out to be a hardware problem with one specific computer. */
+
+	if (isnan(result)) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("\ndtime_now(): %d, %d -> %.3f + %.3f -> NAN!!!\n\n", sec, ns, x1, x2);
+	}
+
+#if DEBUG
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("dtime_now() returns %.3f\n", result);
+#endif
+
+	return (result);
+#endif
+}
+
+
+/* end xmit.c */
+
+
+
diff --git a/xmit.h b/xmit.h
new file mode 100755
index 0000000..7b98ddb
--- /dev/null
+++ b/xmit.h
@@ -0,0 +1,24 @@
+
+
+#ifndef XMIT_H
+#define XMIT_H 1
+
+#include "audio.h"	/* for struct audio_s */
+
+
+extern void xmit_init (struct audio_s *p_modem);
+
+extern void xmit_set_txdelay (int channel, int value);
+
+extern void xmit_set_persist (int channel, int value);
+
+extern void xmit_set_slottime (int channel, int value);
+
+extern void xmit_set_txtail (int channel, int value);
+
+extern double dtime_now (void);
+
+#endif
+
+/* end xmit.h */
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-hamradio/direwolf.git



More information about the pkg-hamradio-commits mailing list