Scandum 6 роки тому
батько
коміт
52eef6142e
55 змінених файлів з 9675 додано та 4294 видалено
  1. 650 195
      COPYING
  2. 28 3
      NEWS
  3. 56 30
      TODO
  4. 711 221
      docs/help.html
  5. 158 0
      mods/igr.mods
  6. 3 3
      src/Makefile.in
  7. 60 27
      src/advertise.c
  8. 287 435
      src/buffer.c
  9. 36 12
      src/chat.c
  10. 1 0
      src/class.c
  11. 693 452
      src/config.c
  12. 50 48
      src/cursor.c
  13. 651 0
      src/daemon.c
  14. 52 16
      src/data.c
  15. 3 3
      src/debug.c
  16. 758 263
      src/draw.c
  17. 35 8
      src/event.c
  18. 46 20
      src/files.c
  19. 420 237
      src/help.c
  20. 2 2
      src/history.c
  21. 328 284
      src/input.c
  22. 65 11
      src/line.c
  23. 20 6
      src/list.c
  24. 1 1
      src/log.c
  25. 174 105
      src/main.c
  26. 393 108
      src/mapper.c
  27. 188 61
      src/math.c
  28. 82 32
      src/misc.c
  29. 1 1
      src/msdp.c
  30. 184 2
      src/nest.c
  31. 27 25
      src/net.c
  32. 14 9
      src/parse.c
  33. 29 28
      src/path.c
  34. 6 7
      src/port.c
  35. 11 13
      src/regex.c
  36. 44 5
      src/scan.c
  37. 569 171
      src/screen.c
  38. 51 19
      src/session.c
  39. 31 58
      src/show.c
  40. 114 95
      src/split.c
  41. 421 152
      src/substitute.c
  42. 25 13
      src/system.c
  43. 365 317
      src/tables.c
  44. 67 47
      src/telopt_client.c
  45. 0 6
      src/telopt_server.c
  46. 49 16
      src/terminal.c
  47. 314 78
      src/text.c
  48. 467 215
      src/tintin.h
  49. 6 11
      src/tokenize.c
  50. 74 54
      src/trigger.c
  51. 489 158
      src/update.c
  52. 9 8
      src/utf8.c
  53. 28 34
      src/utils.c
  54. 148 123
      src/variable.c
  55. 181 46
      src/vt102.c

+ 650 - 195
COPYING

@@ -1,193 +1,635 @@
-GNU GENERAL PUBLIC LICENSE
-Version 3, 29 June 2007
-
-Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-Preamble
-The GNU General Public License is a free, copyleft license for software and other kinds of works.
-
-The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
-
-To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.
-
-For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
-
-Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.
-
-For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.
-
-Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.
-
-Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.
-
-The precise terms and conditions for copying, distribution and modification follow.
-
-TERMS AND CONDITIONS
-0. Definitions.
-“This License” refers to version 3 of the GNU General Public License.
-
-“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
-
-“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.
-
-To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.
-
-A “covered work” means either the unmodified Program or a work based on the Program.
-
-To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
-
-To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
-
-An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
-
-1. Source Code.
-The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.
-
-A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
-
-The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
-
-The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
-
-The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
-
-The Corresponding Source for a work in source code form is that same work.
-
-2. Basic Permissions.
-All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
-
-You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
-
-Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
-
-3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
-
-When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
-
-4. Conveying Verbatim Copies.
-You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
-
-You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
-
-5. Conveying Modified Source Versions.
-You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
-
-a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
-b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
-c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
-d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
-A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
-
-6. Conveying Non-Source Forms.
-You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
-
-a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
-b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
-c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
-d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
-e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
-A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
-
-A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
-
-“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
-
-If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
-
-The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
-
-Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
-
-7. Additional Terms.
-“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
-
-When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
-
-Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
-
-a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
-b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
-c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
-d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
-e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
-f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
-All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
-
-If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
-
-Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
-
-8. Termination.
-You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
-
-However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
-
-9. Acceptance Not Required for Having Copies.
-You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
-
-10. Automatic Licensing of Downstream Recipients.
-Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
-
-An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
-
-You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
-
-11. Patents.
-A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”.
-
-A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
-
-Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
-
-In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
-
-If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
-
-If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
-
-A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
-
-Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
-
-12. No Surrender of Others' Freedom.
-If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
-
-13. Use with the GNU Affero General Public License.
-Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.
-
-14. Revised Versions of this License.
-The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.
-
-If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
-
-Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
-
-15. Disclaimer of Warranty.
-THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-16. Limitation of Liability.
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-17. Interpretation of Sections 15 and 16.
-If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
-
-END OF TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Programs
-If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
 
     <one line to give the program's name and a brief idea of what it does.>
     Copyright (C) <year>  <name of author>
@@ -204,16 +646,29 @@ To do so, attach the following notices to the program. It is safest to attach th
 
     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
 Also add information on how to contact you by electronic and paper mail.
 
-If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
 
     <program>  Copyright (C) <year>  <name of author>
     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
     This is free software, and you are welcome to redistribute it
     under certain conditions; type `show c' for details.
-The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”.
-
-You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>.
 
-The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <https://www.gnu.org/licenses/why-not-lgpl.html>.
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.

+ 28 - 3
NEWS

@@ -1,14 +1,39 @@
 Due to continuous improvements old tintin scripts aren't always compatible
 with new versions. This document tries to list most compatibility conflicts.
 
-TinTin++ 2.02.0
+TinTin++ 2.01.92
+----------------
+
+01) The #buffer get command has been changed to use the new row notation
+    standard.
+
+02) The #split command has been changed to where it sets the number of
+    top split lines if only one argument is given.
+
+    The 2nd argument sets the number of bottom split lines which defaults
+    to 1. The 3rd and 4th argument set the left and right split lines.
+    
+03) #killall gives a warning that it should be changed to #kill all
+
+04) #forall has been removed.
+
+05) %. and %+ should no longer be used in regular expressions. This will
+    allow expansion of tintin's make-shift regex syntax in the future.
+
+    Use {.} and {.+} and {.?+} instead.
+
+06) Added a notice for people still using $variable[] instead of
+    *variable[]. I'll change $variable[] eventually to be identical to
+    $variable[%*].
+
+TinTin++ 2.01.90
 ---------------
 
-02) You'll have to issue #map color reset and #map legend reset to get
+01) You'll have to issue #map color reset and #map legend reset to get
     your old map files straightened out. Once the map is saved again
     it should work without issues.
 
-03) There were two big problems that needed to be addressed to continue
+02) There were two big problems that needed to be addressed to continue
     smooth development.
 
     1. In VT100 row 1 is the top line, while in tintin row 1 is the split

+ 56 - 30
TODO

@@ -1,54 +1,78 @@
 * STUFF THAT IS PROBABLY GONNA GET DONE
 
-  - #loop {1} {1000} {cnt} {#var test[$cnt] 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-}
+    well if i map run somewhere with a door, i won't open it
+    it just plots the directions, and doesn't use the exit commands unless you have nofollow flipped the other way
 
-  - add #map destroy area <area>
+  - add color stripped MSDP option.
 
-  - make format %X take unicode
+  - resize update on attach and detach?
 
-  - more potent long-click handling including held down ticks.
+  - dummy sessions
 
-  - look into discord api / arachnos
+  - highlight files being read with verbose
 
-  - finish mth true color conversion code
+  - #draw table {1;2;3;4} {a;b;c;d} 
+    #draw graph
+    #draw button
 
-  - #bell off option.
+  - %b %B mode. (compress + mode)
 
-  - draw example in #help
+  - proper vt100 skip detection for improper color codes.
 
-  - make *draw_room more comprehensible
+  - I'll look and see if I can make { match both a { and \x7B as it's an annoying issue to debug.
 
-  - give SUB_FIX its own flag to avoid confusion.
+  - Add vtcode detection to skip semicolons? May be a security risk.
 
-  - #detach command.
+  - Add VT100 filter to make outside sources respect terminal size constraints, also needed to run bash in vsplit mode.
 
-  - Improve #draw including 0000 mode.
+  - list changes don't appear to fire variable update events:
 
-  - Add gtd->cursor_level to avoid saving an already saved cursor position
+  - Add #draw error handling.
 
-  - add maze flag for maze handling.
+  - Avoid/Hide flag exits coloring.
 
-  - #buffer copy {lines} with strip option. #line strip?
+  - #map merge / sync support.
 
+  - #map Landmark support.
 
+  - map sandbox mode support.
 
-  - add class specific debug
+  - map jump n e s w support.
 
-  - add screen draw options to draw lines and boxes.
+  - -⸜   diagonal void room handling
+      \
 
-  - Work on VT2020
+  - custom exit colors.
 
-  - add tunnel void utility to #map interface. (covered by #map move tho)
+  - Would be awesome if #map move/at could take the speedwalking format
 
-  - make #split static and fix wrapping of split line on horizontal shrink.
+  - Option to just save config.
 
-  - add dont_echo check
+  - Remote script loading
 
-  - look into storing exit info in the grid map.
+  - Work on VT2020 protocol (mouse click)
 
-  - look into utf-8 text obfuscation.
+  - #MAP exit commands do not work if nofollow flag is on. 
+
+  - make #path load bi-directional.
+
+  - class bla assign {stuff} feature?
+
+  - more potent long-click handling including held down ticks.
+
+  - look into discord api / arachnos
+
+  - make *draw_room more comprehensible
 
-  - look into socket code (linger?) and socket error handling
+  - #detach command.
+
+  - add maze flag for maze handling.
+
+  - add class specific debug
+
+  - make #split static and fix wrapping of split line on horizontal shrink.
+
+  - look into utf-8 text obfuscation.
 
   - better color syntax highlighting configuration.
 
@@ -100,6 +124,7 @@
   - Check socket code for odd behavior on mass connect
 
   - Fix arrow key up history recall overwriting the prompt (partial redesign)
+    Auto prompt fixing on overwrite.
 
   - https://tintin.sourceforge.io/forum/viewtopic.php?f=3&t=2614&p=10665 inherit local vars
 
@@ -109,8 +134,6 @@
 
   - more accurate map mouse reporting
 
-  - swap %D and %X
-
   - https://tintin.sourceforge.io/forum/viewtopic.php?f=5&t=2600 map viewing mode.
 
   - #buffer events
@@ -187,25 +210,25 @@
 
 * ROADMAP
 
+  - attach/detach support
+
   - #screen support
 
   - tintin commander
 
-  - syntax highlighter (more work to be done)
-
   - sixtel graphics
 
   - #window support
 
   - clickable link support
 
-  - buttons
-
 
 --------------------------------------------------------------------------------
 
 * STUFF THAT MIGHT BE IMPLEMENTED
 
+  - ability to #read entire directories.
+
   - fix ctrl-r ctrl-d / ctrl-c / enter
 
   - Option to run #script in background.
@@ -345,6 +368,8 @@
 
 - Keep last input visible with repeat enter enabled.
 
+- would be nice to have "#map list" show the variable being filtered on, instead of defaulting to roomname ie, I'm currently writing a script to save "#map list {roomnote} {{\w+}}" into a variable, then map get roomnote for each item in the list and display it...would be nice if when listing for a roomnote, it showed the roomnote
+
 -------------------------------------------------------------------------------
 
 * NEW STUFF
@@ -355,3 +380,4 @@
 - map sharing
 - cursor extension for vim
 - interactive script tutorial
+

Різницю між файлами не показано, бо вона завелика
+ 711 - 221
docs/help.html


+ 158 - 0
mods/igr.mods

@@ -1,6 +1,164 @@
 Sep 2019        2.01.92
 ------------------------------------------------------------------------------
 
+mapper.c        Added #map dig <exit> {{roomid}{<key>}} {NEW} support.
+                Added #map goto {{roomid}{<key>}} {DIG} support.
+
+mapper.c        Added the roomid field to map rooms. When using #map goto or
+                #map run on a roomid regular expressions cannot be used.
+                Roomid's are case sensitive, must be matched exactly, and
+                are expected to be unique.
+
+daemon.c        Added #daemon {kill|list}. See #help daemon.
+
+daemon.c        Added #daemon {attach|detach}. See #help daemon.
+
+main.c          Can launch tt++ with the -R argument to reattach.
+
+data.c          Added #info MCCP option.
+
+draw.c          Added TUBED drawing option.
+
+scan.c          Added #scan FILE {commands} option. See #help scan.
+
+map.c           Can provide multiple ; separated commands to #map move at
+                once. For example: #map move {n;e;s;w}
+
+main.c          Added support for starting tintin with telnet://host:port
+
+draw.c          Added ANSI, UNICODE, JEWELED and FILLED prefixes.
+
+tables.c        Removed #forall command.
+
+mapper.c        Added #map center <x> <y> <z> to set the center of the map
+                viewer, default is 0 0 0.
+
+split.c         Added SCREEN SPLIT and SCREEN UNSPLIT events.
+
+nest.c          Added VARIABLE UPDATED event.
+
+draw.c          Added the BUMPED prefix to prefix a scroll region box with an
+                enter. You create a scroll region box by using 0 as the top
+                row argument. Treat the other arguments as if the top row
+                is set to 1.
+
+draw.c          Finished rounded box drawing, to get the old behavior use
+                the CIRCLED prefix.
+
+config.c        Added #config {mouse tracking} {on|off|debug|info|debug info}
+                to enable/disable info/debugging for all mouse clicks.
+                Use #config {mouse tracking} {on} to reset to normal.
+
+help.c          Updated #help regex to state that %. and %+ should no longer
+                be used. This will allow future expansion of tintin's regex
+                syntax. Use {.} and {.+} and {.?+} instead.
+
+variable.c      Added support for %+10w and %-10w to #format. Negative numbers
+                subtract the given value from the screen width when wrapping.
+
+text.c          Added initial tab displaying support.
+
+config.c        Added TAB WIDTH config option.
+
+nest.c          Added a notice for people still using $variable[] instead of
+                *variable[]. I'll change $variable[] eventually to be
+                identical to $variable[%*].
+
+math.c          Added .. ellipsis support to define ranges in tables. It can
+                also be used in #math to join two 32 bit integers into a 64
+                bit integer. 
+
+terminal.c      Now initializing a more advanced keyboard mode upon startup,
+                if a macro stops working check if you have to redefine it to
+                a new sequence.
+
+math.c          By default tintin does a string vs regex comparison when
+                using == and !=. Added === and !== to do string vs string
+                comparisons.
+
+config.c        Made configurations less lazy, making it easier to detect
+                when settings aren't properly inherited or changed globally.
+
+screen.c        Added #screen scroll option, similar to #split but uses square
+                notation to define the scroll region.
+
+screen.c        Added #screen fill option.
+
+terminal.c      Added SCREEN ROTATE PORTRAIT and SCREEN ROTATE LANDSCAPE
+                events.
+
+mapper.c        #map offset will now rescale along with the window.
+
+split.c         The #split command has been changed to where it sets the
+                number of top split lines if only one argument is given.
+
+                The 2nd argument sets the number of bottom split lines which
+                defaults to 1. The 3rd and 4th argument set the left and right
+                split lines and when used will automatically enable vertical
+                split mode.
+
+                Split initalization will use unicode if #config charset is
+                set to utf-8
+
+buffer.c        The #buffer get syntax has been changed to treat 1 as the
+                oldest line in the buffer, and -1 as the most recent.
+                Unfortunately that's not backward compatible, but it's in
+                line with the other row behavior changes, so at least it
+                will be consistent.
+
+text.c          With #split enabled and #config wordwrap set to a positive
+                value tintin++ will attempt a vertical split.
+
+draw.c          More feature complete, see #help draw.
+
+vt100.c         Added better cursor save/restore handling.
+
+update.c        Added support for centisecond delays (0.01) and moved ticks to
+                deciseconds (0.1). 
+
+                Reduced input and session polling lag from 25 ms to 5 ms,
+                which comes at a slight increase in cpu usage.
+
+draw.c          Added TEED prefix for #draw, subsequently #draw middle and
+                #draw center have been removed as they did the same and
+                were somewhat confusing terms.
+
+misc.c          Added some #bell options, see #help bell.
+                #bell {flash|focus|margin|ring|volume} {argument}
+
+variable.c      #format %X now will also take unicode characters as input.
+                #echo %X ┓ displays 2513 and #showme \u2513 displays ┓
+
+path.c          #path run no longer uses delays but uses its own timer system,
+                you can use #path stop to stop running, and if you enter
+                #path run again you'll continue where you stopped.
+
+trigger.c       Named delays are now automatically converted to one shot
+                triggers. #undelay will use #untick if the argument begins
+                with a letter.
+
+mapper.c        Changed #map destroy to #map destroy {area|world} {arg}
+
+files.c         Added {READ ERROR} and {WRITE ERROR} events.
+
+trigger.c       Added #line oneshot support to all triggers and variables.
+
+line.c          Added #line oneshot to create oneshot triggers. Right now
+                only works for #ticker.
+
+line.c          Added #line debug to execute given line in debug mode.
+
+substitute.c    #config color is now taken into account when displaying
+                color codes, downgrading colors as needed.
+
+tables.c        When using color names in highlights all lowercase color names
+                will be dark by default, while capitalized color names
+                will be light. So 'blue' is dark while 'Blue' is light. This
+                allows using 'b Blue' for a light blue background.
+
+draw.c          Added the ability to prefix a draw option with a color name
+                or color code to draw colored lines and boxes.
+
 Aug 2019        2.01.91
 ------------------------------------------------------------------------------
 vt100.c         Added %2 rows %3 cols arguments to the VT100 SCROLL REGION

+ 3 - 3
src/Makefile.in

@@ -20,7 +20,7 @@
 
 DEFINES = -D_GNU_SOURCE @DEFS@
 
-CC = @CC@ -Wall
+CC = @CC@ -g
 MAKE = @MAKE@
 
 prefix = @prefix@
@@ -58,13 +58,13 @@ FFLAGS= $(F1) $(F6)
 
 
 OFILES = \
-files.o help.o strhash.o input.o main.o misc.o net.o parse.o debug.o \
+files.o help.o trigger.o input.o main.o misc.o net.o parse.o debug.o \
 update.o history.o vt102.o terminal.o text.o memory.o math.o split.o \
 system.o mapper.o tables.o buffer.o event.o tokenize.o chat.o utf8.o \
 advertise.o list.o forkpty.o utils.o line.o data.o variable.o msdp.o \
 port.o scan.o telopt_client.o screen.o cursor.o nest.o show.o mccp.o \
 telopt_server.o draw.o log.o path.o session.o class.o config.o ssl.o \
-regex.o trigger.o substitute.o
+regex.o substitute.o daemon.o
 
 default: all
 

+ 60 - 27
src/advertise.c

@@ -52,6 +52,7 @@ struct advertisement_type advertisement_table[] =
 		"<078>\"Have you ever come upon a place on the Net that's so incredible that you\n"
 		"<078>can't believe such entertainment is free? This MUD will blow your mind with\n"
 		"<078>its marvelous attention to detail and incredible role-playing atmosphere!\"\n"
+		"\n"
 		"<078>  -- Yahoo! Wild Web Rides\n"
 		"\n"
                 "<178>To connect to Lost Souls enter: #session ls lostsouls.org 23\n"
@@ -84,6 +85,7 @@ struct advertisement_type advertisement_table[] =
 		"<078>optional PK and arena PvP, extensive character customization options, player\n"
 		"<078>lineages, clans, customizable player houses, item crafting, extensively\n"
 		"<078>customizable UI, Mud Sound Protocol (MSP), MSDP, and so much more.\n"
+		"\n"
 		"<078>This is an a amazing game that you could literally play for a decade and still\n"
 		"<078>discover more - you won't be disappointed!\n"
 		"\n"
@@ -107,7 +109,7 @@ struct advertisement_type advertisement_table[] =
 		100,
 
 		"\n"
-                "<138>                             Lowlands\n"
+                "<138>                    Lowlands  -  http://lolamud.net\n"
 		"\n"
 		"<078>Lowlands is based on the Storms of Time codebase which in turn is based on the\n"
 		"<078>Mortal Realms codebase which was established in 1993. Lowlands has many unique\n"
@@ -123,6 +125,7 @@ struct advertisement_type advertisement_table[] =
 
 		"\n"
 		"<138>Lowlands\n"
+		"<168>http://lolamud.net\n"
 		"\n"
 		"<078>Lowlands is based on the Storms of Time codebase which in turn is based on the Mortal Realms codebase. It's a very polished MUD with many unique systems and features. Its main strength is the inclusion of over 100 detailed area quests which have been custom coded using a powerful mob prog engine.\n"
 		"<078>\n"
@@ -132,35 +135,36 @@ struct advertisement_type advertisement_table[] =
 		"\n"
 	},
 
-/*	{
+	{
 		1400000000,
- 		1800000000,
+		1800000000,
 		100,
-
-		"\n"
-		"<138>                The Last Outpost  -  https://www.last-outpost.com\n"
-		"\n"
-		"<078>The Last Outpost has been serving up adventure since 1992.  Along with\n"
-		"<078>exploring and advancing through the game world, the game offers players the\n"
-		"<078>ability to lay claim to the zones that make up the land.  Once claimed, a zone\n"
-		"<078>can be taxed, and the player making the claim gets to decide policy within the\n"
-		"<078>zone.  Whoever claims the whole world is declared the Leader of the Last\n"
-		"<078>Outpost!  Whether you enjoy hack 'n slash, following quests, PvP, NPK, playing\n"
-		"<078>in clans, or soloing, the Last Outpost has it.\n"
-		"\n"
-		"<178>To connect to The Last Outpost enter: #session lo last-outpost.com 4000\n"
-		"\n",
-
-		"\n"
-		"<138>The Last Outpost\n"
-		"<168>https://www.last-outpost.com\n"
-		"\n"
-		"<078>The Last Outpost has been serving up adventure since 1992.  Along with exploring and advancing through the game world, the game offers players the ability to lay claim to the zones that make up the land.  Once claimed, a zone can be taxed, and the player making the claim gets to decide policy within the zone.  Whoever claims the whole world is declared the Leader of the Last Outpost!  Whether you enjoy hack 'n slash, following quests, PvP, NPK, playing in clans, or soloing, the Last Outpost has it.\n"
-		"\n"
-		"<178>To connect to The Last Outpost enter: #session lo last-outpost.com 4000\n"
 		"\n"
+                "<138>                    3Kingdoms  -  http://3k.org\n"
+                "\n"
+                "<078>Based around the mighty town of Pinnacle, three main realms beckon the player\n"
+                "<078>to explore.  These kingdoms are: Fantasy, a vast medieval realm of orcs, elves,\n"
+                "<078>dragons, and a myriad of other creatures; Science, a post-apocalyptic, war-torn\n"
+                "<078>world set in the not-so-distant future; and Chaos, a transient realm where the\n"
+                "<078>realities of Fantasy and Science collide to produce unimaginable results.\n"
+                "\n"
+                "<078>3Kingdoms combines all these features, and so much more, to give the player an\n"
+                "<078>experience that will stay with them for the rest of their lives.  Come live the\n"
+                "<078>adventure and find out for yourself why 3K is the best there is!\n"
+                "\n"
+                "<178>To connect to 3Kingdoms enter:  #session 3K 3k.org 3000\n"
+                "\n",
+ 
+                "\n"
+                "<138>3Kingdoms\n"
+                "<168>http://3k.org\n"
+                "\n"
+                "<078>Based around the mighty town of Pinnacle, three main realms beckon the player to explore.  These kingdoms are: Fantasy, a vast medieval realm of orcs, elves, dragons, and a myriad of other creatures; Science, a post-apocalyptic, war-torn world set in the not-so-distant future; and Chaos, a transient realm where the realities of Fantasy and Science collide to produce unimaginable results.  3Kingdoms combines all these features, and so much more, to give the player an experience that will stay with them for the rest of their lives.  Come live the adventure and find out for yourself why 3K is the best there is!\n"
+                "\n"
+                "<178>To connect to 3Kingdoms enter:  #session 3K 3k.org 3000\n"
+                "\n",
 	},
-*/
+
 	{
 		1400000000,  /* 2014 */ 
 		1800000000,  /* 2027 */ 
@@ -189,7 +193,6 @@ struct advertisement_type advertisement_table[] =
                 "\n"
                 "<178>To connect to New Worlds Ateraan enter: #session nwa ateraan.com 4002\n"
                 "\n"
-
 	},
 
 	{
@@ -221,6 +224,36 @@ struct advertisement_type advertisement_table[] =
 		"<178>To connect to Primal Darkness enter: #session pd mud.primaldarkness.com 5000\n"
 		"\n"
 	},
+/*	{
+		1400000000,
+ 		1800000000,
+		100,
+
+		"\n"
+		"<138>                The Last Outpost  -  https://www.last-outpost.com\n"
+		"\n"
+		"<078>The Last Outpost has been serving up adventure since 1992.  Along with\n"
+		"<078>exploring and advancing through the game world, the game offers players the\n"
+		"<078>ability to lay claim to the zones that make up the land.  Once claimed, a zone\n"
+		"<078>can be taxed, and the player making the claim gets to decide policy within the\n"
+		"<078>zone.  Whoever claims the whole world is declared the Leader of the Last\n"
+		"<078>Outpost!  Whether you enjoy hack 'n slash, following quests, PvP, NPK, playing\n"
+		"<078>in clans, or soloing, the Last Outpost has it.\n"
+		"\n"
+		"<178>To connect to The Last Outpost enter: #session lo last-outpost.com 4000\n"
+		"\n",
+
+		"\n"
+		"<138>The Last Outpost\n"
+		"<168>https://www.last-outpost.com\n"
+		"\n"
+		"<078>The Last Outpost has been serving up adventure since 1992.  Along with exploring and advancing through the game world, the game offers players the ability to lay claim to the zones that make up the land.  Once claimed, a zone can be taxed, and the player making the claim gets to decide policy within the zone.  Whoever claims the whole world is declared the Leader of the Last Outpost!  Whether you enjoy hack 'n slash, following quests, PvP, NPK, playing in clans, or soloing, the Last Outpost has it.\n"
+		"\n"
+		"<178>To connect to The Last Outpost enter: #session lo last-outpost.com 4000\n"
+		"\n"
+	},
+*/
+
 /*
 	{
 		1400000000,

Різницю між файлами не показано, бо вона завелика
+ 287 - 435
src/buffer.c


+ 36 - 12
src/chat.c

@@ -1,12 +1,11 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
-*   it under the terms of the GNU General Public License as published by      *
-*   the Free Software Foundation; either version 3 of the License, or         *
-*   (at your option) any later version.                                       *
+*   it under the terms of the GNU General Public License version 3 as         *
+*   published by the Free Software Foundation.                                *
 *                                                                             *
 *   This program is distributed in the hope that it will be useful,           *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of            *
@@ -14,8 +13,8 @@
 *   GNU General Public License for more details.                              *
 *                                                                             *
 *                                                                             *
-*   You should have received a copy of the GNU General Public License         *
-*   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
+*   You should have received a copy of the GNU General Public License along   *
+*   with TinTin++.  If not see https://www.gnu.org/licenses/gpl-3.0.txt       *
 ******************************************************************************/
 
 /******************************************************************************
@@ -29,7 +28,6 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <sys/socket.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -39,11 +37,37 @@
 	#include <pthread.h>
 #endif
 
-
-#define CALL_TIMEOUT 5
-#define BLOCK_SIZE 500
+#define CALL_TIMEOUT    5
+#define BLOCK_SIZE    500
 #define DEFAULT_PORT 4050
 
+extern  int chat_new(int s);
+extern void chat_printf(char *format, ...);
+extern  int process_chat_input(struct chat_data *buddy);
+extern void get_chat_commands(struct chat_data *buddy, char *buf, int len);
+extern void chat_name_change(struct chat_data *buddy, char *txt);
+extern void chat_receive_text_everybody(struct chat_data *buddy, char *txt);
+extern void chat_receive_text_personal(struct chat_data *buddy, char *txt);
+extern void chat_receive_text_group(struct chat_data *buddy, char *txt);
+extern void chat_receive_message(struct chat_data *buddy, char *txt);
+extern void chat_receive_snoop_data(struct chat_data *buddy, char *txt);
+extern void ping_response(struct chat_data *ch, char *time);
+extern void request_response(struct chat_data *requester);
+extern void parse_requested_connections(struct chat_data *buddy, char *txt);
+extern void peek_response(struct chat_data *peeker);
+extern void parse_peeked_connections(struct chat_data *buddy, char *txt);
+extern void chat_receive_file(char *arg, struct chat_data *ch);
+extern void send_block(struct chat_data *ch);
+extern  int receive_block(unsigned char *s, struct chat_data *ch, int size);
+extern void deny_file(struct chat_data *ch, char *arg);
+extern void file_denied(struct chat_data *ch, char *txt);
+extern void file_cleanup(struct chat_data *buddy);
+extern  int get_file_size(char *fpath);
+extern void chat_puts(char *arg);
+extern char *fix_file_name(char *name);
+extern struct chat_data *find_buddy(char *arg);
+extern struct chat_data *find_group(char *arg);
+
 DO_COMMAND(do_chat)
 {
 	char cmd[BUFFER_SIZE], arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
@@ -53,7 +77,7 @@ DO_COMMAND(do_chat)
 
 	if (*cmd == 0)
 	{
-		tintin_header(ses, " CHAT COMMANDS ");
+		tintin_header(ses, " CHAT OPTIONS ");
 
 		for (cnt = 0 ; *chat_table[cnt].name != 0 ; cnt++)
 		{
@@ -144,7 +168,7 @@ DO_CHAT(chat_initialize)
 		return;
 	}
 
-	if (listen(sock, 50) == -1)
+	if (listen(sock, 32) == -1)
 	{
 		syserr_printf(gtd->ses, "chat_initialize: listen");
 

+ 1 - 0
src/class.c

@@ -311,6 +311,7 @@ DO_CLASS(class_kill)
 	}
 
 	check_all_events(ses, SUB_ARG, 0, 1, "CLASS DESTROYED", arg1);
+	check_all_events(ses, SUB_ARG, 1, 1, "CLASS DESTROYED %s", arg1, arg1);
 
 	delete_index_list(ses->list[LIST_CLASS], group);
 

+ 693 - 452
src/config.c

@@ -34,7 +34,7 @@ DO_COMMAND(do_configure)
 	struct listnode *node;
 	int index;
 
-	arg = get_arg_in_braces(ses, arg, arg1,  GET_ONE);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
 
 	if (*arg1 == 0)
@@ -47,9 +47,13 @@ DO_COMMAND(do_configure)
 
 			if (node)
 			{
-				tintin_printf2(ses, "[%-14s] [%8s] %s", 
+				strcpy(arg2, "");
+
+				config_table[index].config(ses, arg1, arg2, index);
+
+				tintin_printf2(ses, "[%-14s] [%12s] %s",
 					node->arg1,
-					node->arg2,
+					arg2,
 					strcmp(node->arg2, "ON") == 0 ? config_table[index].msg_on : config_table[index].msg_off);
 			}
 		}
@@ -62,15 +66,26 @@ DO_COMMAND(do_configure)
 		{
 			if (is_abbrev(arg1, config_table[index].name))
 			{
-				if (config_table[index].config(ses, arg2, index) != NULL)
+				if (*arg2)
 				{
-					node = search_node_list(ses->list[LIST_CONFIG], config_table[index].name);
-
-					if (node)
+					if (config_table[index].config(ses, arg1, arg2, index) != NULL)
 					{
-						show_message(ses, LIST_CONFIG, "#CONFIG {%s} HAS BEEN SET TO {%s}.", config_table[index].name, node->arg2);
+						update_node_list(ses->list[LIST_CONFIG], config_table[index].name, arg2, "", "");
+
+						node = search_node_list(ses->list[LIST_CONFIG], config_table[index].name);
+
+						if (node)
+						{
+							show_message(ses, LIST_CONFIG, "#CONFIG {%s} HAS BEEN SET TO {%s}.", config_table[index].name, node->arg2);
+						}
 					}
 				}
+				else
+				{
+					config_table[index].config(ses, arg1, arg2, index);
+
+					show_message(ses, LIST_CONFIG, "#CONFIG {%s} IS SET TO {%s}.", config_table[index].name, arg2);
+				}
 				return ses;
 			}
 		}
@@ -79,711 +94,937 @@ DO_COMMAND(do_configure)
 	return ses;
 }
 
-
-DO_CONFIG(config_speedwalk)
+DO_CONFIG(config_autotab)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		SET_BIT(ses->flags, SES_FLAG_SPEEDWALK);
-	}
-	else if (!strcasecmp(arg, "OFF"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_SPEEDWALK);
-	}
-	else
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+		if (!is_number(arg2))
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {AUTO TAB} <NUMBER>");
+
+			return NULL;
+		}
+
+		if (atoi(arg2) < 1 || atoi(arg2) > 999999)
+		{
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG BUFFER: PROVIDE A NUMBER BETWEEN 1 and 999999");
+
+			return NULL;
+		}
 
-		return NULL;
+		ses->auto_tab = atoi(arg2);
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	sprintf(arg2, "%d", ses->auto_tab);
 
 	return ses;
 }
 
 
-DO_CONFIG(config_verbatim)
+DO_CONFIG(config_buffersize)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		SET_BIT(ses->flags, SES_FLAG_VERBATIM);
-	}
-	else if (!strcasecmp(arg, "OFF"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_VERBATIM);
-	}
-	else
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+		if (!is_number(arg2))
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {BUFFER SIZE} <NUMBER>");
+
+			return NULL;
+		}
 
-		return NULL;
+		switch (atoi(arg2))
+		{
+			case 100:
+			case 1000:
+			case 10000:
+			case 100000:
+			case 1000000:
+				break;
+
+			default:
+				show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG BUFFER: SIZE MUST BE 100, 1000, 10000, 100000, or 1000000.");
+				return NULL;
+		}
+		init_buffer(ses, atoi(arg2));
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	sprintf(arg2, "%d", ses->scroll->size);
 
 	return ses;
 }
 
-DO_CONFIG(config_repeatenter)
+DO_CONFIG(config_charset)
 {
-	if (!strcasecmp(arg, "ON"))
+	if (*arg2)
 	{
-		SET_BIT(ses->flags, SES_FLAG_REPEATENTER);
-	}
-	else if (!strcasecmp(arg, "OFF"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_REPEATENTER);
+		if (is_abbrev(arg2, "AUTO"))
+		{
+			if (strcasestr(gtd->lang, "UTF-8"))
+			{
+				DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+				SET_BIT(ses->charset, CHARSET_FLAG_UTF8);
+			}
+			else if (strcasestr(gtd->lang, "BIG-5"))
+			{
+				DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+				SET_BIT(ses->charset, CHARSET_FLAG_BIG5);
+			}
+			else if (strcasestr(gtd->term, "XTERM"))
+			{
+				DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+				SET_BIT(ses->charset, CHARSET_FLAG_UTF8);
+			}
+			else
+			{
+				DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+			}
+		}
+		else if (is_abbrev(arg2, "BIG-5"))
+		{
+			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+			SET_BIT(ses->charset, CHARSET_FLAG_BIG5);
+		}
+		else if (is_abbrev(arg2, "UTF-8"))
+		{
+			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+			SET_BIT(ses->charset, CHARSET_FLAG_UTF8);
+		}
+		else if (is_abbrev(arg2, "ASCII"))
+		{
+			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+		}
+		else if (is_abbrev(arg2, "BIG2UTF"))
+		{
+			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+			SET_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5TOUTF8);
+		}
+		else if (is_abbrev(arg2, "FANSI"))
+		{
+			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+			SET_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_FANSITOUTF8);
+		}
+		else if (is_abbrev(arg2, "KOI2UTF"))
+		{
+			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+			SET_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_KOI8TOUTF8);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <AUTO|ASCII|BIG-5|FANSI|UTF-8|BIG2UTF|KOI2UTF>", config_table[index].name);
+
+			return NULL;
+		}
 	}
-	else
-	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+	switch (HAS_BIT(ses->charset, CHARSET_FLAG_ALL))
+	{
+		case CHARSET_FLAG_BIG5:
+			strcpy(arg2, "BIG-5");
+			break;
+		case CHARSET_FLAG_UTF8:
+			strcpy(arg2, "UTF-8");
+			break;
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5TOUTF8:
+			strcpy(arg2, "BIG2UTF");
+			break;
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_FANSITOUTF8:
+			strcpy(arg2, "FANSI");
+			break;
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_KOI8TOUTF8:
+			strcpy(arg2, "KOI2UTF");
+			break;
+		default:
+			strcpy(arg2, "ASCII");
+			break;
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
 
 	return ses;
 }
 
-DO_CONFIG(config_commandcolor)
+DO_CONFIG(config_childlock)
 {
-	char buf[BUFFER_SIZE];
-
-	substitute(ses, arg, buf, SUB_COL);
-
-	RESTRING(ses->cmd_color, buf);
+	if (*arg2)
+	{
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(gtd->flags, TINTIN_FLAG_CHILDLOCK);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(gtd->flags, TINTIN_FLAG_CHILDLOCK);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, arg, "", "");
+			return NULL;
+		}
+	}
+	strcpy(arg2, HAS_BIT(gtd->flags, TINTIN_FLAG_CHILDLOCK) ? "ON" : "OFF");
 
 	return ses;
 }
 
-DO_CONFIG(config_commandecho)
+DO_CONFIG(config_colormode)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		SET_BIT(ses->flags, SES_FLAG_ECHOCOMMAND);
-	}
-	else if (!strcasecmp(arg, "OFF"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_ECHOCOMMAND);
-	}
-	else
-	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+	if (*arg2)
+	{	
+		if (is_abbrev(arg2, "NONE") || is_abbrev(arg2, "OFF"))
+		{
+			ses->color = 0;
+		}
+		else if (is_abbrev(arg2, "ANSI"))
+		{
+			ses->color = 16;
+		}
+		else if (is_abbrev(arg2, "256"))
+		{
+			ses->color = 256;
+		}
+		else if (is_abbrev(arg2, "TRUE") || is_abbrev(arg2, "ON") || is_abbrev(arg2, "AUTO"))
+		{
+			ses->color = 4096;
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF|ANSI|256|TRUE>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	strcpy(arg2, ses->color == 0 ? "OFF" : ses->color == 16 ? "ANSI" : ses->color == 256 ? "256" : "TRUE");
 
 	return ses;
 }
 
-DO_CONFIG(config_verbose)
+DO_CONFIG(config_colorpatch)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		SET_BIT(ses->flags, SES_FLAG_VERBOSE);
-	}
-	else if (!strcasecmp(arg, "OFF"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_VERBOSE);
-	}
-	else
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(ses->flags, SES_FLAG_COLORPATCH);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_COLORPATCH);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	strcpy(arg2, HAS_BIT(ses->flags, SES_FLAG_COLORPATCH) ? "ON" : "OFF");
 
 	return ses;
 }
 
-DO_CONFIG(config_wordwrap)
+DO_CONFIG(config_commandcolor)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		ses->wrap = -1;
-	}
-	else if (!strcasecmp(arg, "OFF"))
+	if (*arg2)
 	{
-		ses->wrap = 0;
-	}
-	else if (is_number(arg))
-	{
-		if (atoi(arg) < 1 || atoi(arg) > 10000)
+		if (!get_color_names(ses, arg2, arg1))
 		{
-			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG BUFFER: PROVIDE A NUMBER BETWEEN 1 and 10000");
+			show_error(ses, LIST_CONFIG, "#CONFIG COMMAND COLOR: INVALID COLOR CODE {%s}", arg2);
 
 			return NULL;
 		}
-		ses->wrap = atoi(arg);
-	}
-	else
-	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF|NUMBER>", config_table[index].name);
-
-		return NULL;
+		RESTRING(ses->cmd_color, arg1);
 	}
-
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
-
-	SET_BIT(gtd->flags, TINTIN_FLAG_RESETBUFFER);
+	convert_meta(ses->cmd_color, arg2, SUB_EOL);
 
 	return ses;
 }
 
-DO_CONFIG(config_logmode)
+DO_CONFIG(config_commandecho)
 {
-	if (!strcasecmp(arg, "HTML"))
-	{
-		SET_BIT(ses->logmode, LOG_FLAG_HTML);
-		DEL_BIT(ses->logmode, LOG_FLAG_PLAIN);
-		DEL_BIT(ses->logmode, LOG_FLAG_RAW);
-	}
-	else if (!strcasecmp(arg, "PLAIN"))
+	if (*arg2)
 	{
-		DEL_BIT(ses->logmode, LOG_FLAG_HTML);
-		SET_BIT(ses->logmode, LOG_FLAG_PLAIN);
-		DEL_BIT(ses->logmode, LOG_FLAG_RAW);
-	}
-	else if (!strcasecmp(arg, "RAW"))
-	{
-		DEL_BIT(ses->logmode, LOG_FLAG_HTML);
-		DEL_BIT(ses->logmode, LOG_FLAG_PLAIN);
-		SET_BIT(ses->logmode, LOG_FLAG_RAW);
-	}
-	else
-	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG LOG <HTML|PLAIN|RAW>");
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(ses->flags, SES_FLAG_ECHOCOMMAND);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_ECHOCOMMAND);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	strcpy(arg2, HAS_BIT(ses->flags, SES_FLAG_ECHOCOMMAND) ? "ON" : "OFF");
 
 	return ses;
 }
 
-DO_CONFIG(config_buffersize)
+DO_CONFIG(config_connectretry)
 {
-	if (!is_number(arg))
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {BUFFER SIZE} <NUMBER>");
-
-		return NULL;
-	}
+		if (!is_number(arg2))
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {CONNECT RETRY} <NUMBER>");
 
-	if (atoi(arg) < 100 || atoi(arg) > 999999)
-	{
-		show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG BUFFER: PROVIDE A NUMBER BETWEEN 100 and 999999");
+			return NULL;
+		}
+		else if (atof(arg2) < 0 || atof(arg2) > 10000)
+		{
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG CONNECT RETRY: PROVIDE A NUMBER BETWEEN 0.0 and 10000.0");
 
-		return NULL;
+			return NULL;
+		}
+		gts->connect_retry = atoll(arg2) * 1000000LL;
 	}
 
-	init_buffer(ses, atoi(arg));
-
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	sprintf(arg2, "%6.1Lf", (long double) gts->connect_retry / 1000000);
 
 	return ses;
 }
 
-DO_CONFIG(config_scrolllock)
+DO_CONFIG(config_convertmeta)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		SET_BIT(ses->flags, SES_FLAG_SCROLLLOCK);
-	}
-	else if (!strcasecmp(arg, "OFF"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_SCROLLLOCK);
-	}
-	else
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(ses->flags, SES_FLAG_CONVERTMETA);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_CONVERTMETA);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	strcpy(arg2, HAS_BIT(ses->flags, SES_FLAG_CONVERTMETA) ? "ON" : "OFF");
 
 	return ses;
 }
 
-DO_CONFIG(config_connectretry)
+DO_CONFIG(config_debugtelnet)
 {
-	if (!is_number(arg))
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {CONNECT RETRY} <NUMBER>");
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(ses->telopts, TELOPT_FLAG_DEBUG);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->telopts, TELOPT_FLAG_DEBUG);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-
-	gts->connect_retry = atoll(arg) * 1000000LL;
-
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	strcpy(arg2, HAS_BIT(ses->telopts, TELOPT_FLAG_DEBUG) ? "ON" : "OFF");
 
 	return ses;
 }
 
-DO_CONFIG(config_packetpatch)
+DO_CONFIG(config_historysize)
 {
-	if (is_abbrev("AUTO", arg))
+	if (*arg2)
 	{
-		gts->check_output = 0;
-
-		update_node_list(ses->list[LIST_CONFIG], config_table[index].name, "AUTO", "", "");
-
-		SET_BIT(ses->flags, SES_FLAG_AUTOPATCH);
-
-		return ses;
-	}
+		if (!is_number(arg2))
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {HISTORY SIZE} <NUMBER>");
 
-	if (!is_number(arg))
-	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {PACKET PATCH} <NUMBER>");
+			return NULL;
+		}
 
-		return NULL;
-	}
+		if (atoi(arg2) < 0 || atoi(arg2) > 9999)
+		{
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG HISTORY: PROVIDE A NUMBER BETWEEN 0 and 9999");
 
-	if (atof(arg) < 0 || atof(arg) > 10)
-	{
-		show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG PACKET PATCH: PROVIDE A NUMBER BETWEEN 0.00 and 10.00");
+			return NULL;
+		}
 
-		return NULL;
+		gtd->history_size = atoi(arg2);
 	}
-
-	DEL_BIT(ses->flags, SES_FLAG_AUTOPATCH);
-
-	gts->check_output = (unsigned long long) (tintoi(arg) * 1000000ULL);
-
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	sprintf(arg2, "%d", gtd->history_size);
 
 	return ses;
 }
 
-
-DO_CONFIG(config_historysize)
+DO_CONFIG(config_inheritance)
 {
-	if (!is_number(arg))
-	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {HISTORY SIZE} <NUMBER>");
-	}
-
-	if (atoi(arg) < 0 || atoi(arg) > 9999)
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG HISTORY: PROVIDE A NUMBER BETWEEN 0 and 9999");
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(gtd->flags, TINTIN_FLAG_INHERITANCE);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(gtd->flags, TINTIN_FLAG_INHERITANCE);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-
-	gtd->history_size = atoi(arg);
-
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	strcpy(arg2, HAS_BIT(gtd->flags, TINTIN_FLAG_INHERITANCE) ? "ON" : "OFF");
 
 	return ses;
 }
 
-DO_CONFIG(config_tintinchar)
-{
-	gtd->tintin_char = arg[0];
-
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, arg, "", "");
-
-	return ses;
-}
 
-DO_CONFIG(config_verbatimchar)
+DO_CONFIG(config_loglevel)
 {
-	gtd->verbatim_char = arg[0];
+	if (*arg2)
+	{
+		if (is_abbrev(arg2, "LOW"))
+		{
+			SET_BIT(ses->logmode, LOG_FLAG_LOW);
+		}
+		else if (is_abbrev(arg2, "HIGH"))
+		{
+			DEL_BIT(ses->logmode, LOG_FLAG_LOW);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <LOW|HIGH>", config_table[index].name);
 
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, arg, "", "");
+			return NULL;
+		}
+	}
+	strcpy(arg2, HAS_BIT(ses->logmode, LOG_FLAG_LOW) ? "LOW" : "HIGH");
 
 	return ses;
 }
 
-DO_CONFIG(config_repeatchar)
+
+DO_CONFIG(config_logmode)
 {
-	gtd->repeat_char = arg[0];
+	if (*arg2)
+	{
+		if (is_abbrev(arg2, "HTML"))
+		{
+			SET_BIT(ses->logmode, LOG_FLAG_HTML);
+			DEL_BIT(ses->logmode, LOG_FLAG_PLAIN);
+			DEL_BIT(ses->logmode, LOG_FLAG_RAW);
+		}
+		else if (is_abbrev(arg2, "PLAIN"))
+		{
+			DEL_BIT(ses->logmode, LOG_FLAG_HTML);
+			SET_BIT(ses->logmode, LOG_FLAG_PLAIN);
+			DEL_BIT(ses->logmode, LOG_FLAG_RAW);
+		}
+		else if (is_abbrev(arg2, "RAW"))
+		{
+			DEL_BIT(ses->logmode, LOG_FLAG_HTML);
+			DEL_BIT(ses->logmode, LOG_FLAG_PLAIN);
+			SET_BIT(ses->logmode, LOG_FLAG_RAW);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG LOG <HTML|PLAIN|RAW>");
 
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, arg, "", "");
+			return NULL;
+		}
+	}
+	strcpy(arg2, HAS_BIT(ses->logmode, LOG_FLAG_HTML) ? "HTML" : HAS_BIT(ses->logmode, LOG_FLAG_PLAIN) ? "PLAIN" : "RAW");
 
 	return ses;
 }
 
-DO_CONFIG(config_debugtelnet)
+
+DO_CONFIG(config_mccp)
 {
-	if (!strcasecmp(arg, "ON"))
+	if (*arg2)
 	{
-		SET_BIT(ses->telopts, TELOPT_FLAG_DEBUG);
-	}
-	else if (!strcasecmp(arg, "OFF"))
-	{
-		DEL_BIT(ses->telopts, TELOPT_FLAG_DEBUG);
-	}
-	else
-	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(ses->flags, SES_FLAG_MCCP);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_MCCP);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	strcpy(arg2, HAS_BIT(ses->flags, SES_FLAG_MCCP) ? "ON" : "OFF");
 
 	return ses;
 }
 
-DO_CONFIG(config_telnet)
+
+DO_CONFIG(config_mousetracking)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		DEL_BIT(ses->telopts, TELOPT_FLAG_DEBUG);
-		SET_BIT(ses->flags, SES_FLAG_TELNET);
-	}
-	else if (!strcasecmp(arg, "OFF"))
+	if (*arg2)
 	{
-		DEL_BIT(ses->telopts, TELOPT_FLAG_DEBUG);
-		DEL_BIT(ses->flags, SES_FLAG_TELNET);
+		if (is_abbrev(arg2, "ON"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_MOUSEDEBUG);
+			DEL_BIT(ses->flags, SES_FLAG_MOUSEINFO);
+			SET_BIT(gtd->flags, TINTIN_FLAG_MOUSETRACKING);
+			print_stdout("\e[?1000h\e[?1002h\e[?1004h\e[?1006h");
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			if (HAS_BIT(gtd->flags, TINTIN_FLAG_MOUSETRACKING))
+			{
+				DEL_BIT(ses->flags, SES_FLAG_MOUSEDEBUG);
+				DEL_BIT(ses->flags, SES_FLAG_MOUSEINFO);
+				DEL_BIT(gtd->flags, TINTIN_FLAG_MOUSETRACKING);
+				print_stdout("\e[?1000l\e[?1002l\e[?1004l\e[?1006l");
+			}
+		}
+		else if (is_abbrev(arg2, "DEBUG"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_MOUSEINFO);
+			SET_BIT(ses->flags, SES_FLAG_MOUSEDEBUG);
+			SET_BIT(gtd->flags, SES_FLAG_MOUSETRACKING);
+			print_stdout("\e[?1000h\e[?1002h\e[?1004h\e[?1006h");
+		}
+		else if (is_abbrev(arg2, "DEBUG INFO"))
+		{
+			SET_BIT(ses->flags, SES_FLAG_MOUSEDEBUG);
+			SET_BIT(ses->flags, SES_FLAG_MOUSEINFO);
+			SET_BIT(gtd->flags, TINTIN_FLAG_MOUSETRACKING);
+			print_stdout("\e[?1000h\e[?1002h\e[?1004h\e[?1006h");
+		}
+		else if (is_abbrev(arg2, "INFO"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_MOUSEDEBUG);
+			SET_BIT(ses->flags, SES_FLAG_MOUSEINFO);
+			SET_BIT(gtd->flags, TINTIN_FLAG_MOUSETRACKING);
+			print_stdout("\e[?1000h\e[?1002h\e[?1004h\e[?1006h");
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF|DEBUG|INFO|DEBUG INFO>", config_table[index].name);
+
+			return NULL;
+		}
 	}
-	else if (!strcasecmp(arg, "DEBUG"))
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_MOUSETRACKING))
 	{
-		SET_BIT(ses->telopts, TELOPT_FLAG_DEBUG);
-		SET_BIT(ses->flags, SES_FLAG_TELNET);
+		switch (HAS_BIT(ses->flags, SES_FLAG_MOUSEDEBUG|SES_FLAG_MOUSEINFO))
+		{
+			case 0:
+				strcpy(arg2, "ON");
+				break;
+			case SES_FLAG_MOUSEDEBUG:
+				strcpy(arg2, "DEBUG");
+				break;
+			case SES_FLAG_MOUSEINFO:
+				strcpy(arg2, "INFO");
+				break;
+			default:
+				strcpy(arg2, "DEBUG INFO");
+				break;
+		}
 	}
 	else
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF|DEBUG>", config_table[index].name);
-
-		return NULL;
+		strcpy(arg2, "OFF");
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
-
 	return ses;
 }
 
-DO_CONFIG(config_convertmeta)
+
+DO_CONFIG(config_packetpatch)
 {
-	if (!strcasecmp(arg, "ON"))
+	if (*arg2)
 	{
-		SET_BIT(ses->flags, SES_FLAG_CONVERTMETA);
+		if (is_abbrev(arg2, "AUTO"))
+		{
+			ses->packet_patch = 0;
+
+			SET_BIT(ses->flags, SES_FLAG_AUTOPATCH);
+		}
+		else if (!is_number(arg2))
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {PACKET PATCH} <NUMBER>");
+
+			return NULL;
+		}
+		else if (atof(arg2) < 0 || atof(arg2) > 10)
+		{
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG PACKET PATCH: PROVIDE A NUMBER BETWEEN 0.00 and 10.00");
+
+			return NULL;
+		}
+		else
+		{
+			DEL_BIT(ses->flags, SES_FLAG_AUTOPATCH);
+
+			ses->packet_patch = (unsigned long long) (tintoi(arg2) * 1000000ULL);
+		}
 	}
-	else if (!strcasecmp(arg, "OFF"))
+
+	if (HAS_BIT(ses->flags, SES_FLAG_AUTOPATCH))
 	{
-		DEL_BIT(ses->flags, SES_FLAG_CONVERTMETA);
+		strcpy(arg2, "AUTO");
 	}
 	else
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
-
-		return NULL;
+		sprintf(arg2, "%4.2Lf", (long double) ses->packet_patch / 1000000);
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
-
 	return ses;
 }
 
-DO_CONFIG(config_loglevel)
+DO_CONFIG(config_pid)
 {
-	if (!strcasecmp(arg, "LOW"))
-	{
-		SET_BIT(ses->logmode, LOG_FLAG_LOW);
-	}
-	else if (!strcasecmp(arg, "HIGH"))
-	{
-		DEL_BIT(ses->logmode, LOG_FLAG_LOW);
-	}
-	else
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <LOW|HIGH>", config_table[index].name);
+		if (!is_number(arg2))
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {PID} <NUMBER>");
+			
+			return NULL;
+		}
+		else if (atoi(arg2) < 0 || atoi(arg2) > 4194303)
+		{
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG PID: PROVIDE A NUMBER BETWEEN 3 and 4194304");
 
-		return NULL;
+			return NULL;
+		}
+		else
+		{
+			gtd->detach_pid = atoi(arg2);
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	sprintf(arg2, "%d", gtd->detach_pid);
 
 	return ses;
 }
-
-DO_CONFIG(config_colorpatch)
+	
+DO_CONFIG(config_randomseed)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		SET_BIT(ses->flags, SES_FLAG_COLORPATCH);
-	}
-	else if (!strcasecmp(arg, "OFF"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_COLORPATCH);
-	}
-	else
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+		if (is_abbrev(arg2, "AUTO"))
+		{
+			seed_rand(ses, utime());
+		}
+		else if (is_number(arg2))
+		{
+			seed_rand(ses, get_number(ses, arg2));
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <AUTO|NUMBER>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	sprintf(arg2, "%llu", ses->rand);
 
 	return ses;
 }
 
-DO_CONFIG(config_mccp)
+DO_CONFIG(config_repeatchar)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		SET_BIT(ses->flags, SES_FLAG_MCCP);
-	}
-	else if (!strcasecmp(arg, "OFF"))
+	if (*arg2)
 	{
-		DEL_BIT(ses->flags, SES_FLAG_MCCP);
-	}
-	else
-	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+		if (!ispunct((int) arg2[0]))
+		{
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG REPEAT CHAR: INVALID CHARACTER {%c}", arg2[0]);
 
-		return NULL;
+			return NULL;
+		}
+
+		gtd->repeat_char = arg2[0];
+
+		arg2[1] = 0;
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+
+	sprintf(arg2, "%c", gtd->repeat_char);
 
 	return ses;
 }
 
-DO_CONFIG(config_autotab)
+DO_CONFIG(config_repeatenter)
 {
-	if (!is_number(arg))
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {AUTO TAB} <NUMBER>");
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(ses->flags, SES_FLAG_REPEATENTER);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_REPEATENTER);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
+	strcpy(arg2, HAS_BIT(ses->flags, SES_FLAG_REPEATENTER) ? "ON" : "OFF");
 
-	if (atoi(arg) < 1 || atoi(arg) > 999999)
-	{
-		show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG BUFFER: PROVIDE A NUMBER BETWEEN 1 and 999999");
+	return ses;
+}
 
-		return NULL;
-	}
 
-	ses->auto_tab = atoi(arg);
+DO_CONFIG(config_screenreader)
+{
+	if (*arg2)
+	{
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(ses->flags, SES_FLAG_SCREENREADER);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_SCREENREADER);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+			return NULL;
+		}
+	}
+	strcpy(arg2, HAS_BIT(ses->flags, SES_FLAG_SCREENREADER) ? "ON" : "OFF");
 
 	return ses;
 }
 
-DO_CONFIG(config_charset)
+
+
+
+DO_CONFIG(config_scrolllock)
 {
-	if (!strcasecmp(arg, "AUTO"))
-	{
-		if (strcasestr(gtd->lang, "UTF-8"))
+	if (*arg2)
+	{	
+		if (is_abbrev(arg2, "ON"))
 		{
-			strcpy(arg, "UTF-8");
+			SET_BIT(ses->flags, SES_FLAG_SCROLLLOCK);
 		}
-		else if (strcasestr(gtd->lang, "BIG-5"))
+		else if (is_abbrev(arg2, "OFF"))
 		{
-			strcpy(arg, "BIG-5");
+			DEL_BIT(ses->flags, SES_FLAG_SCROLLLOCK);
 		}
 		else
 		{
-			strcpy(arg, "ASCII");
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+
+			return NULL;
 		}
 	}
+	strcpy(arg2, HAS_BIT(ses->flags, SES_FLAG_SCROLLLOCK) ? "ON" : "OFF");	
 
-	if (!strcasecmp(arg, "BIG5"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_CHARSETS);
-		SET_BIT(ses->flags, SES_FLAG_BIG5);
-	}
-	else if (!strcasecmp(arg, "BIG-5"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_CHARSETS);
-		SET_BIT(ses->flags, SES_FLAG_BIG5);
-	}
-	else if (!strcasecmp(arg, "UTF-8"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_CHARSETS);
-		SET_BIT(ses->flags, SES_FLAG_UTF8);
-	}
-	else if (!strcasecmp(arg, "ASCII"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_CHARSETS);
-	}
-	else if (!strcasecmp(arg, "BIG2UTF"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_CHARSETS);
-		SET_BIT(ses->flags, SES_FLAG_UTF8|SES_FLAG_BIG5TOUTF8);
-	}
-	else if (!strcasecmp(arg, "FANSI"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_CHARSETS);
-		SET_BIT(ses->flags, SES_FLAG_UTF8|SES_FLAG_FANSITOUTF8);
-	}
-	else if (!strcasecmp(arg, "KOI2UTF"))
-	{
-		DEL_BIT(ses->flags, SES_FLAG_CHARSETS);
-		SET_BIT(ses->flags, SES_FLAG_UTF8|SES_FLAG_KOI8TOUTF8);
-	}
-	else
+	return ses;
+}
+
+DO_CONFIG(config_speedwalk)
+{
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <AUTO|ASCII|BIG-5|FANSI|UTF-8|BIG2UTF|KOI2UTF>", config_table[index].name);
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(ses->flags, SES_FLAG_SPEEDWALK);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_SPEEDWALK);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	strcpy(arg2, HAS_BIT(ses->flags, SES_FLAG_SPEEDWALK) ? "ON" : "OFF");
 
 	return ses;
 }
 
-DO_CONFIG(config_colormode)
+DO_CONFIG(config_tabwidth)
 {
-	if (!strcasecmp(arg, "AUTO"))
+	if (*arg2)
 	{
-		if (strcasestr(gtd->term, "truecolor"))
+		if (is_abbrev(arg2, "AUTO"))
 		{
-			strcpy(arg, "TRUE");
+			ses->tab_width = 8;
 		}
-		else if (!strcasecmp(gtd->term, "xterm") || strcasestr(gtd->term, "256color"))
+		else if (!is_number(arg2))
 		{
-			strcpy(arg, "256");
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {TAB WIDTH} <NUMBER>");
+
+			return NULL;
+		}
+		else if (atof(arg2) < 1 || atof(arg2) > 16)
+		{
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG TAB WIDTH: PROVIDE A NUMBER BETWEEN 1 and 16");
+
+			return NULL;
 		}
 		else
 		{
-			strcpy(arg, "ANSI");
+			ses->tab_width = (int) tintoi(arg2);
 		}
 	}
+	sprintf(arg2, "%d", ses->tab_width);
 
-	if (!strcasecmp(arg, "NONE"))
-	{
-		strcpy(arg, "NONE");
-		DEL_BIT(ses->flags, SES_FLAG_ANSICOLOR|SES_FLAG_256COLOR|SES_FLAG_TRUECOLOR);
-	}
-	else if (!strcasecmp(arg, "ANSI"))
-	{
-		strcpy(arg, "ANSI");
-		SET_BIT(ses->flags, SES_FLAG_ANSICOLOR);
-		DEL_BIT(ses->flags, SES_FLAG_256COLOR|SES_FLAG_TRUECOLOR);
-	}
-	else if (!strcasecmp(arg, "256"))
-	{
-		SET_BIT(ses->flags, SES_FLAG_ANSICOLOR|SES_FLAG_256COLOR);
-		DEL_BIT(ses->flags, SES_FLAG_TRUECOLOR);
-	}
-	else if (!strcasecmp(arg, "TRUE"))
-	{
-		strcpy(arg, "TRUE");
-		SET_BIT(ses->flags, SES_FLAG_ANSICOLOR|SES_FLAG_256COLOR|SES_FLAG_TRUECOLOR);
-	}
-	else
+	return ses;
+}
+	
+DO_CONFIG(config_telnet)
+{
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <AUTO|NONE|ANSI|256|TRUE>", config_table[index].name);
+		if (is_abbrev(arg2, "ON"))
+		{
+			DEL_BIT(ses->telopts, TELOPT_FLAG_DEBUG);
+			SET_BIT(ses->flags, SES_FLAG_TELNET);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->telopts, TELOPT_FLAG_DEBUG);
+			DEL_BIT(ses->flags, SES_FLAG_TELNET);
+		}
+		else if (is_abbrev(arg2, "DEBUG"))
+		{
+			SET_BIT(ses->telopts, TELOPT_FLAG_DEBUG);
+			SET_BIT(ses->flags, SES_FLAG_TELNET);
+		}
+		else if (is_abbrev(arg2, "INFO"))
+		{
+			SET_BIT(ses->telopts, TELOPT_FLAG_DEBUG);
+			SET_BIT(ses->flags, SES_FLAG_TELNET);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF|DEBUG|INFO>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, arg, "", "");
+	strcpy(arg2, HAS_BIT(ses->telopts, TELOPT_FLAG_DEBUG) ? "DEBUG" : HAS_BIT(ses->flags, SES_FLAG_TELNET) ? "ON" : "OFF");
 
 	return ses;
 }
 
 
-DO_CONFIG(config_screenreader)
+DO_CONFIG(config_tintinchar)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		SET_BIT(ses->flags, SES_FLAG_SCREENREADER);
-	}
-	else if (!strcasecmp(arg, "OFF"))
+	if (*arg2)
 	{
-		DEL_BIT(ses->flags, SES_FLAG_SCREENREADER);
-	}
-	else
-	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+		if (!ispunct((int) arg2[0]))
+		{
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG TINTIN CHAR: INVALID CHARACTER {%c}", arg2[0]);
 
-		return NULL;
+			return NULL;
+		}
+
+		gtd->tintin_char = arg2[0];
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	sprintf(arg2, "%c", gtd->tintin_char);
 
 	return ses;
 }
 
-DO_CONFIG(config_inheritance)
+DO_CONFIG(config_verbatim)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		SET_BIT(gtd->flags, TINTIN_FLAG_INHERITANCE);
-	}
-	else if (!strcasecmp(arg, "OFF"))
-	{
-		DEL_BIT(gtd->flags, TINTIN_FLAG_INHERITANCE);
-	}
-	else
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(ses->flags, SES_FLAG_VERBATIM);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_VERBATIM);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	strcpy(arg2, HAS_BIT(ses->flags, SES_FLAG_VERBATIM) ? "ON" : "OFF");
 
 	return ses;
 }
 
-DO_CONFIG(config_randomseed)
+DO_CONFIG(config_verbatimchar)
 {
-	if (!strcasecmp(arg, "AUTO"))
-	{
-		strcpy(arg, "AUTO");
-
-		seed_rand(ses, utime());
-	}
-	else if (is_number(arg))
+	if (*arg2)
 	{
-		seed_rand(ses, get_number(ses, arg));
-	}
-	else
-	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <AUTO|NUMBER>", config_table[index].name);
+		if (!ispunct((int) arg2[0]))
+		{
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG VERBATIM CHAR: INVALID CHARACTER {%c}", arg2[0]);
 
-		return NULL;
+			return NULL;
+		}
+		gtd->verbatim_char = arg2[0];
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, arg, "", "");
+	sprintf(arg2, "%c", gtd->verbatim_char);
 
 	return ses;
 }
 
-DO_CONFIG(config_mousetracking)
+
+DO_CONFIG(config_verbose)
 {
-	if (!strcasecmp(arg, "ON"))
+	if (*arg2)
 	{
-		SET_BIT(gtd->flags, TINTIN_FLAG_MOUSETRACKING);
-		printf("\e[?1000h\e[?1002h\e[?1004h\e[?1006h");
-	}
-	else if (!strcasecmp(arg, "OFF"))
-	{
-		DEL_BIT(gtd->flags, TINTIN_FLAG_MOUSETRACKING);
-		printf("\e[?1000l\e[?1002l\e[?1004l\e[?1006l");
-	}
-	else
-	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(ses->flags, SES_FLAG_VERBOSE);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_VERBOSE);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	strcpy(arg2, HAS_BIT(ses->flags, SES_FLAG_VERBOSE) ? "ON" : "OFF");
 
 	return ses;
 }
 
-DO_CONFIG(config_childlock)
+DO_CONFIG(config_wordwrap)
 {
-	if (!strcasecmp(arg, "ON"))
-	{
-		SET_BIT(gtd->flags, TINTIN_FLAG_CHILDLOCK);
-	}
-	else if (!strcasecmp(arg, "OFF"))
-	{
-		DEL_BIT(gtd->flags, TINTIN_FLAG_CHILDLOCK);
-	}
-	else
+	if (*arg2)
 	{
-		show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+		if (is_abbrev(arg2, "ON"))
+		{
+			SET_BIT(ses->flags, SES_FLAG_WORDWRAP);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_WORDWRAP);
+		}
+		else
+		{
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
 
-		return NULL;
+			return NULL;
+		}
 	}
-	update_node_list(ses->list[LIST_CONFIG], config_table[index].name, capitalize(arg), "", "");
+	strcpy(arg2, HAS_BIT(ses->flags, SES_FLAG_WORDWRAP) ? "ON" : "OFF");
 
 	return ses;
 }

+ 50 - 48
src/cursor.c

@@ -79,7 +79,7 @@ DO_COMMAND(do_cursor)
 				break;
 			}
 		}
-		tintin_printf(ses, "#ERROR: #CURSOR {%s} IS NOT A VALID OPTION.", capitalize(all));
+		show_error(ses, LIST_COMMAND, "#ERROR: #CURSOR {%s} IS NOT A VALID OPTION.", capitalize(all));
 	}
 	return ses;
 }
@@ -97,7 +97,7 @@ int inputline_str_str_len(int start, int end)
 			break;
 		}
 
-		if (HAS_BIT(gtd->ses->flags, SES_FLAG_UTF8) && is_utf8_head(&gtd->input_buf[raw_cnt]))
+		if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&gtd->input_buf[raw_cnt]))
 		{
 			raw_cnt += get_utf8_width(&gtd->input_buf[raw_cnt], &width);
 
@@ -136,7 +136,7 @@ int inputline_raw_str_len(int start, int end)
 			break;
 		}
 
-		if (HAS_BIT(gtd->ses->flags, SES_FLAG_UTF8) && is_utf8_head(&gtd->input_buf[raw_cnt]))
+		if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&gtd->input_buf[raw_cnt]))
 		{
 			raw_cnt += get_utf8_width(&gtd->input_buf[raw_cnt], &width);
 			ret_cnt += width;
@@ -165,7 +165,7 @@ int inputline_str_raw_len(int start, int end)
 			break;
 		}
 
-		if (HAS_BIT(gtd->ses->flags, SES_FLAG_UTF8) && is_utf8_head(&gtd->input_buf[raw_cnt]))
+		if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&gtd->input_buf[raw_cnt]))
 		{    
 			tmp_cnt = get_utf8_width(&gtd->input_buf[raw_cnt], &width);
 
@@ -210,6 +210,13 @@ int inputline_cur_str_len(void)
 	return inputline_str_str_len(gtd->input_hid, gtd->input_hid + inputline_max_str_len());
 }
 
+// Get the position of the cursor
+
+int inputline_cur_pos(void)
+{
+	return gtd->input_off + gtd->input_pos - gtd->input_hid;
+}
+
 // Check for invalid characters.
 
 int inputline_str_chk(int offset, int totlen)
@@ -218,7 +225,7 @@ int inputline_str_chk(int offset, int totlen)
 
 	while (offset < totlen)
 	{
-		if (HAS_BIT(gtd->ses->flags, SES_FLAG_BIG5))
+		if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_BIG5))
 		{
 			if (HAS_BIT(gtd->input_buf[offset], 128) && (unsigned char) gtd->input_buf[offset] < 255)
 			{
@@ -241,7 +248,7 @@ int inputline_str_chk(int offset, int totlen)
 				offset += 1;
 			}
 		}
-		else if (HAS_BIT(gtd->ses->flags, SES_FLAG_UTF8))
+		else if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8))
 		{
 			if (is_utf8_head(&gtd->input_buf[offset]))
 			{
@@ -450,7 +457,7 @@ DO_CURSOR(cursor_delete)
 		return;
 	}
 
-	if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && is_big5(&gtd->input_buf[gtd->input_cur]))
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && is_big5(&gtd->input_buf[gtd->input_cur]))
 	{
 		gtd->input_len--;
 
@@ -458,7 +465,7 @@ DO_CURSOR(cursor_delete)
 
 		input_printf("\e[2P");
 	}
-	else if (HAS_BIT(ses->flags, SES_FLAG_UTF8))
+	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
 	{
 		size = get_utf8_width(&gtd->input_buf[gtd->input_cur], &width);
 
@@ -520,7 +527,7 @@ DO_CURSOR(cursor_delete_word_left)
 
 	while (gtd->input_cur > 0 && gtd->input_buf[gtd->input_cur - 1] != ' ')
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_UTF8))
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
 		{
 			if (is_utf8_tail(&gtd->input_buf[gtd->input_cur]))
 			{
@@ -635,6 +642,7 @@ DO_CURSOR(cursor_enter)
 	gtd->input_cur    = 0;
 	gtd->input_pos    = 0;
 	gtd->input_off    = 1;
+	gtd->input_hid    = 0;
 	gtd->macro_buf[0] = 0;
 	gtd->input_tmp[0] = 0;
 
@@ -694,7 +702,7 @@ DO_CURSOR(cursor_history_next)
 
 		for (root->update++ ; root->update < root->used ; root->update++)
 		{
-			if (*gtd->input_buf && find(ses, root->list[root->update]->arg1, gtd->input_buf, SUB_NONE, SUB_NONE))
+			if (*gtd->input_buf && find(ses, root->list[root->update]->arg1, gtd->input_buf, SUB_NONE, REGEX_FLAG_NONE))
 			{
 				break;
 			}
@@ -758,7 +766,7 @@ DO_CURSOR(cursor_history_prev)
 
 		for (root->update-- ; root->update >= 0 ; root->update--)
 		{
-			if (*gtd->input_buf && find(ses, root->list[root->update]->arg1, gtd->input_buf, SUB_NONE, SUB_NONE))
+			if (*gtd->input_buf && find(ses, root->list[root->update]->arg1, gtd->input_buf, SUB_NONE, REGEX_FLAG_NONE))
 			{
 				break;
 			}
@@ -868,7 +876,7 @@ DO_CURSOR(cursor_history_find)
 
 	push_call("cursor_history_find(%s)", gtd->input_buf);
 
-	if (HAS_BIT(ses->flags, SES_FLAG_UTF8|SES_FLAG_BIG5))
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5))
 	{
 		if (inputline_str_chk(0, gtd->input_len) == FALSE)
 		{
@@ -877,17 +885,17 @@ DO_CURSOR(cursor_history_find)
 		}
 	}
 
-	gtd->quiet_level++;
+	gtd->level->quiet++;
 
 	for (root->update = root->used - 1 ; root->update >= 0 ; root->update--)
 	{
-		if (*gtd->input_buf && find(ses, root->list[root->update]->arg1, gtd->input_buf, SUB_NONE, SUB_NONE))
+		if (*gtd->input_buf && find(ses, root->list[root->update]->arg1, gtd->input_buf, SUB_NONE, REGEX_FLAG_NONE))
 		{
 			break;
 		}
 	}
 
-	gtd->quiet_level--;
+	gtd->level->quiet--;
 
 	if (root->update >= 0)
 	{
@@ -948,7 +956,7 @@ DO_CURSOR(cursor_left)
 
 	if (gtd->input_cur > 0)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5))
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5))
 		{
 			gtd->input_cur--;
 			gtd->input_pos--;
@@ -961,7 +969,7 @@ DO_CURSOR(cursor_left)
 				input_printf("\e[1D");
 			}
 		}
-		else if (HAS_BIT(ses->flags, SES_FLAG_UTF8))
+		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
 		{
 			gtd->input_cur--;
 
@@ -1021,7 +1029,7 @@ DO_CURSOR(cursor_left_word)
 	{
 		gtd->input_cur--;
 
-		if (HAS_BIT(ses->flags, SES_FLAG_UTF8))
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
 		{
 			if (!is_utf8_tail(&gtd->input_buf[gtd->input_cur]))
 			{
@@ -1070,21 +1078,23 @@ DO_CURSOR(cursor_redraw_input)
 	}
 	else
 	{
-//		cursor_redraw_line(ses, "");
-
-		input_printf("\e[1G\e[0K%s%s\e[0K", ses->more_output, gtd->input_buf);
-
+/*		if (*gtd->ses->scroll->input)
+		{
+			input_printf("\e[G%s", gtd->ses->scroll->input);
+		}*/
+		cursor_redraw_line(ses, "");
+/*
 		gtd->input_cur = gtd->input_len;
 
 		gtd->input_pos = gtd->input_len % gtd->screen->cols;
-
+*/
 	}
 }
 
 
 DO_CURSOR(cursor_redraw_line)
 {
-	if (HAS_BIT(ses->flags, SES_FLAG_UTF8|SES_FLAG_BIG5))
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5))
 	{
 		if (inputline_str_chk(0, gtd->input_len) == FALSE)
 		{
@@ -1150,7 +1160,7 @@ DO_CURSOR(cursor_right)
 
 	if (gtd->input_cur < gtd->input_len)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && (gtd->input_buf[gtd->input_cur] & 128) == 128)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && (gtd->input_buf[gtd->input_cur] & 128) == 128)
 		{
 			if (gtd->input_cur + 1 < gtd->input_len && gtd->input_buf[gtd->input_cur+1])
 			{
@@ -1160,7 +1170,7 @@ DO_CURSOR(cursor_right)
 				input_printf("\e[2C");
 			}
 		}
-		else if (HAS_BIT(ses->flags, SES_FLAG_UTF8))
+		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
 		{
 			gtd->input_cur += get_utf8_width(&gtd->input_buf[gtd->input_cur], &width);
 
@@ -1208,7 +1218,7 @@ DO_CURSOR(cursor_right_word)
 
 	while (gtd->input_cur < gtd->input_len && gtd->input_buf[gtd->input_cur] != ' ')
 	{
-		if (!HAS_BIT(ses->flags, SES_FLAG_UTF8) || (gtd->input_buf[gtd->input_cur] & 192) != 128)
+		if (!HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) || (gtd->input_buf[gtd->input_cur] & 192) != 128)
 		{
 			gtd->input_pos++;
 		}
@@ -1260,13 +1270,17 @@ DO_CURSOR(cursor_info)
 int cursor_tab_add(int input_now, int stop_after_first)
 {
 	struct listroot *root = gtd->ses->list[LIST_TAB];
+	struct listnode *node;
+
 	char tab[BUFFER_SIZE];
 
 	if (!HAS_BIT(root->flags, LIST_FLAG_IGNORE))
 	{
 		for (root->update = 0 ; root->update < root->used ; root->update++)
 		{
-			substitute(gtd->ses, root->list[root->update]->arg1, tab, SUB_VAR|SUB_FUN);
+			node = root->list[root->update];
+
+			substitute(gtd->ses, node->arg1, tab, SUB_VAR|SUB_FUN);
 
 			if (!strncmp(tab, &gtd->input_buf[input_now], strlen(&gtd->input_buf[input_now])))
 			{
@@ -1276,6 +1290,11 @@ int cursor_tab_add(int input_now, int stop_after_first)
 				}
 				insert_node_list(gtd->ses->list[LIST_COMMAND], tab, "", "", "");
 
+				if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+				{
+					delete_node_list(gtd->ses, LIST_TAB, node);
+				}
+
 				if (stop_after_first)
 				{
 					return TRUE;
@@ -1295,25 +1314,9 @@ int cursor_auto_tab_add(int input_now, int stop_after_first)
 
 	line_cnt = 0;
 
-	scroll_cnt = gtd->ses->scroll->row;
-
-	do
+	for (scroll_cnt = gtd->ses->scroll->used - 1 ; scroll_cnt > 0 ; scroll_cnt--)
 	{
-		if (scroll_cnt == gtd->ses->scroll->max -1)
-		{
-			scroll_cnt = 0;
-		}
-		else
-		{
-			scroll_cnt++;
-		}
-
-		if (gtd->ses->scroll->buffer[scroll_cnt] == NULL)
-		{
-			break;
-		}
-
-		if (str_hash_grep(gtd->ses->scroll->buffer[scroll_cnt], FALSE))
+		if (HAS_BIT(gtd->ses->scroll->buffer[scroll_cnt]->flags, BUFFER_FLAG_GREP))
 		{
 			continue;
 		}
@@ -1323,7 +1326,7 @@ int cursor_auto_tab_add(int input_now, int stop_after_first)
 			break;
 		}
 
-		strip_vt102_codes(gtd->ses->scroll->buffer[scroll_cnt], buf);
+		strip_vt102_codes(gtd->ses->scroll->buffer[scroll_cnt]->str, buf);
 
 		arg = buf;
 
@@ -1362,7 +1365,6 @@ int cursor_auto_tab_add(int input_now, int stop_after_first)
 
 		}
 	}
-	while (scroll_cnt != gtd->ses->scroll->row);
 
 	return FALSE;
 }

+ 651 - 0
src/daemon.c

@@ -0,0 +1,651 @@
+/******************************************************************************
+*   This file is part of TinTin++                                             *
+*                                                                             *
+*   Copyright 2004-2019 Igor van den Hoven                                    *
+*                                                                             *
+*   TinTin++ is free software; you can redistribute it and/or modify          *
+*   it under the terms of the GNU General Public License as published by      *
+*   the Free Software Foundation; either version 3 of the License, or         *
+*   (at your option) any later version.                                       *
+*                                                                             *
+*   This program is distributed in the hope that it will be useful,           *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of            *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
+*   GNU General Public License for more details.                              *
+*                                                                             *
+*                                                                             *
+*   You should have received a copy of the GNU General Public License         *
+*   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
+******************************************************************************/
+
+/******************************************************************************
+*               (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                  *
+*                                                                             *
+*                     coded by Igor van den Hoven 2007                        *
+******************************************************************************/
+
+#include "tintin.h"
+
+#ifdef HAVE_PTY_H
+#include <pty.h>
+#else
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#endif
+#include <fcntl.h>  
+#include <dirent.h>
+#include <termios.h>
+#include <sys/un.h>
+
+int get_daemon_dir(struct session *ses, char *filename);
+
+DO_COMMAND(do_daemon)
+{
+	char arg1[BUFFER_SIZE];
+	int cnt;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0)
+	{
+		tintin_header(ses, " DAEMON OPTIONS ");
+
+		for (cnt = 0 ; *daemon_table[cnt].fun != NULL ; cnt++)
+		{
+			if (*daemon_table[cnt].desc)
+			{
+				tintin_printf2(ses, "  [%-13s] %s", daemon_table[cnt].name, daemon_table[cnt].desc);
+			}
+		}
+		tintin_header(ses, "");
+
+		return ses;
+	}
+	else
+	{
+		for (cnt = 0 ; *daemon_table[cnt].name ; cnt++)
+		{
+			if (is_abbrev(arg1, daemon_table[cnt].name))
+			{
+				break;
+			}
+		}
+
+		if (*daemon_table[cnt].name == 0)
+		{
+			do_daemon(ses, "");
+		}
+		else
+		{
+			daemon_table[cnt].fun(ses, arg);
+		}
+	}
+
+	return ses;
+}
+
+
+DO_DAEMON(daemon_attach)
+{
+	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], filename[BUFFER_SIZE], sock_file[BUFFER_SIZE];
+	struct dirent **dirlist;
+	struct sockaddr_un addr_un;
+	int size, index, pid, error, repeat = 0;
+	struct timeval timeout;
+	fd_set wds, rds;
+
+	timeout.tv_sec  = 0;
+	timeout.tv_usec = 100000;
+
+	if (gtd->attach_sock)
+	{
+		show_error(ses, LIST_COMMAND, "#DAEMON ATTACH: YOU ARE ALREADY ATTACHED TO {%s}.", gtd->attach_file);
+
+		return;
+	}
+	sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (!get_daemon_dir(ses, filename))
+	{
+		return;
+	}
+
+	start:
+
+	size = scandir(filename, &dirlist, 0, alphasort);
+
+	if (size == -1)
+	{
+		syserr_printf(ses, "do_attach: scandir:");
+
+		return;
+	}
+
+	for (*arg2 = index = pid = 0 ; index < size ; index++)
+	{
+		if (strlen(dirlist[index]->d_name) > 2)
+		{
+			if (*arg1)
+			{
+				if (!strstr(dirlist[index]->d_name, arg1))
+				{
+					continue;
+				}
+			}
+			arg = strchr(dirlist[index]->d_name, '.');
+
+			if (arg)
+			{
+				*arg = 0;
+
+				strcpy(arg2, dirlist[index]->d_name);
+
+				arg = strchr(dirlist[index]->d_name, '_');
+
+				if (arg)
+				{
+					pid = atoi(arg + 1);
+				}
+				break;
+			}
+		}
+	}
+
+	for (index = 0 ; index < size ; index++)
+	{
+		free(dirlist[index]);
+	}
+	free(dirlist);
+
+	if (pid == 0)
+	{
+		if (HAS_BIT(gtd->flags, TINTIN_FLAG_DAEMONIZE))
+		{
+			daemon_detach(ses, arg1);
+
+			return;
+		}
+
+		if (*arg1 && ++repeat < 10)
+		{
+			usleep(2000);
+
+			goto start;
+		}
+
+		if (*arg1)
+		{
+			show_message(ses, LIST_COMMAND, "#DAEMON ATTACH: UNABLE TO FIND DAEMON FILE {%s} IN {%s}.", arg1, filename);
+		}
+		else
+		{
+			show_message(ses, LIST_COMMAND, "#DAEMON ATTACH: NO AVAILABLE DAEMON FILES FOUND IN {%s}.", filename);
+		}
+
+		return;
+	}
+
+	DEL_BIT(gtd->flags, TINTIN_FLAG_DAEMONIZE);
+
+	sprintf(sock_file, "%s/%s.s", filename, arg2);
+
+	if (access(sock_file, F_OK) == -1)
+	{
+		show_error(ses, LIST_COMMAND, "#ERROR: DAEMON ATTACH: FILE {%s} CANNOT BE ACCESSED.", sock_file);
+
+		return;
+	}
+
+	if (kill((pid_t) pid, 0) == -1)
+	{
+		show_error(ses, LIST_COMMAND, "#ERROR: DAEMON ATTACH: REMOVING INVALID DAEMON FILE {%s}.", sock_file);
+
+		remove(sock_file);
+
+		if (*arg1 == 0)
+		{
+			goto start;
+		}
+		return;
+	}
+
+	memset(&addr_un, 0, sizeof(addr_un));
+
+	if (strlen(sock_file) >= sizeof(addr_un.sun_path))
+	{
+		show_error(ses, LIST_COMMAND, "#ERROR: #DAEMON ATTACH: {%s} FILENAME EXCEEDS MAXIMUM LENGTH OF %d.", filename, sizeof(addr_un.sun_path));
+
+		return;
+	}
+
+	if (pid == getpid())
+	{
+		show_error(ses, LIST_COMMAND, "#ERROR: #DAEMON ATTACH: {%s} CANNOT ATTACH TO ITSELF.", filename);
+		
+		return;
+	}
+
+	gtd->attach_file = restringf(gtd->attach_file, "%s", sock_file);
+	gtd->attach_pid  = pid;
+	gtd->attach_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+
+	if (gtd->attach_sock == -1)
+	{
+		syserr_printf(ses, "do_attach: %s: socket:");
+
+		gtd->attach_sock = 0;
+
+		return;
+	}
+
+	strcpy(addr_un.sun_path, sock_file);
+	addr_un.sun_family = AF_UNIX;
+
+	show_message(ses, LIST_COMMAND, "#DAEMON ATTACH: CONNECTING {%d} TO {%d} {%s}", getpid(), gtd->attach_pid, sock_file);
+
+	if (connect(gtd->attach_sock, (struct sockaddr *)&addr_un, sizeof(addr_un)) == -1)
+	{
+		syserr_printf(ses, "do_attach: %s: connect:", sock_file);
+
+		gtd->attach_sock = close(gtd->attach_sock);
+
+		return;
+	}
+
+
+	FD_ZERO(&wds);
+
+	FD_SET(gtd->attach_sock, &wds);
+
+	error = select(FD_SETSIZE, NULL, &wds, NULL, &timeout);
+
+	if (error < 0)
+	{
+		syserr_printf(ses, "do_attach: select wds:");
+
+		show_error(ses, LIST_COMMAND, "#ERROR: #DAEMON ATTACH: UNABLE TO WRITE TO {%s}.", sock_file);
+
+		gtd->attach_sock = close(gtd->attach_sock);
+
+		return;
+	}
+
+	if (!FD_ISSET(gtd->attach_sock, &wds))
+	{
+		show_error(ses, LIST_COMMAND, "#ERROR: #DAEMON ATTACH: UNABLE TO WRITE TO {%s}.", sock_file);
+
+		gtd->attach_sock = close(gtd->attach_sock);
+
+		return;
+	}
+
+	FD_ZERO(&rds);
+	FD_SET(gtd->attach_sock, &rds);
+
+	error = select(FD_SETSIZE, &rds, NULL, NULL, &timeout);
+
+	if (error < 0)
+	{
+		syserr_printf(ses, "do_attach: select rds:");
+
+		gtd->attach_sock = close(gtd->attach_sock);
+
+		return;
+	}
+
+	if (error == 0)
+	{
+		tintin_printf2(ses, "do_attach: select rds: timeout");
+
+		gtd->attach_sock = close(gtd->attach_sock);
+		
+		return;
+	}
+
+	return;
+}
+
+DO_DAEMON(daemon_detach)
+{
+	char arg1[BUFFER_SIZE], filename[BUFFER_SIZE];
+	struct sockaddr_un addr_un;
+	pid_t pid, sid;
+	int dev_null;
+
+	sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (gtd->detach_port)
+	{
+		if (gtd->detach_sock)
+		{
+			show_message(gtd->ses, LIST_COMMAND, "#DAEMON DETACH: DETACHING FROM {%s}", gtd->detach_file);
+
+//			kill((pid_t) gtd->detach_sock, SIGTSTP);
+
+//			print_stdout("%c", (char) 255);
+
+			gtd->detach_sock = close(gtd->detach_sock);
+		}
+		else
+		{
+			show_error(gtd->ses, LIST_COMMAND, "#DAEMON DETACH: ALREADY FULLY DETACHED.");
+		}
+		return;
+	}
+
+	pid = fork();
+
+	if (pid < 0)
+	{
+		syserr_printf(ses, "do_detatch: fork:");
+
+		return;
+	}
+
+	if (pid > 0)
+	{
+		if (HAS_BIT(gtd->flags, TINTIN_FLAG_DAEMONIZE))
+		{
+			DEL_BIT(gtd->flags, TINTIN_FLAG_DAEMONIZE);
+
+//			usleep(100000);
+
+			daemon_attach(ses, *arg1 ? arg1 : "pid");
+
+			return;
+		}
+		reset_terminal(gtd->ses);
+
+		print_stdout("\e[r\e[%d;%dH", gtd->screen->rows, 1);
+
+		_exit(0);
+	}
+
+	DEL_BIT(gtd->flags, TINTIN_FLAG_DAEMONIZE);
+
+	sid = setsid();
+
+	if (sid < 0)
+	{
+		syserr_printf(ses, "do_detach: setsid:");
+		return;
+	}
+
+	if (!get_daemon_dir(ses, filename))
+	{
+		return;
+	}
+
+	memset(&addr_un, 0, sizeof(addr_un));
+
+	cat_sprintf(filename, "/%s_%d.s", *arg1 ? arg1 : "pid", getpid());
+
+	if (strlen(filename) >= sizeof(addr_un.sun_path))
+	{
+		tintin_printf(ses, "#DAEMON DETACH: FILE NAME LENGTH OF {%s} EXCEEDS MAXIMUM OF %d.", filename, sizeof(addr_un.sun_path));
+
+		return;
+	}
+
+	strcpy(addr_un.sun_path, filename);
+	addr_un.sun_family = AF_UNIX;
+
+	show_message(ses, LIST_COMMAND, "#DAEMON DETACH: DAEMONIZING PROCESS %d AS {%s}", getpid(), filename);
+
+	gtd->detach_port = socket(AF_UNIX, SOCK_STREAM, 0);
+
+	if (gtd->detach_port <= 0)
+	{
+		syserr_printf(ses, "do_detach: socket:");
+
+		return;
+	}
+
+	if (bind(gtd->detach_port, (struct sockaddr *) &addr_un, sizeof(struct sockaddr_un)) < 0)
+	{
+		syserr_printf(ses, "do_detach: bind:");
+
+		gtd->detach_port = close(gtd->detach_port);
+
+		return;
+	}
+
+	if (listen(gtd->detach_port, 32) < 0)
+	{
+		syserr_printf(ses, "do_detach: listen:");
+
+		gtd->detach_port = close(gtd->detach_port);
+
+		return;
+	}
+
+	dev_null = open("/dev/null", O_RDWR, 0);
+
+	if (dev_null == -1)
+	{
+		syserr_printf(ses, "daemon_detach: dev_null open:");
+
+		return;
+	}
+
+	if (dup2(dev_null, STDIN_FILENO) == -1)
+	{
+		syserr_printf(ses, "daemon_detach: dup2 STDIN-fileno:");
+	}
+
+//	dup2(dev_null, STDOUT_FILENO);
+//	dup2(dev_null, STDERR_FILENO);
+
+	close(dev_null);
+
+	gtd->detach_file = restringf(gtd->detach_file, "%s", filename);
+
+	return;
+}
+
+DO_DAEMON(daemon_input)
+{
+	char arg1[BUFFER_SIZE], out[BUFFER_SIZE];
+	int size;
+
+	if (*arg == 0)
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #DAEMON INPUT <TEXT>");
+
+		return;
+	}
+
+	if (gtd->attach_sock <= 0)
+	{
+		show_error(ses, LIST_COMMAND, "#DAEMON INPUT: YOU MUST BE ATTACHED TO A DAEMON TO SEND INPUT.");
+
+		return;
+	}
+
+	get_arg_in_braces(ses, arg, arg1, GET_ALL);
+
+	size = substitute(ses, arg1, out, SUB_VAR|SUB_FUN|SUB_COL|SUB_ESC|SUB_EOL);
+
+	if (write(gtd->attach_sock, out, size) < 0)
+	{
+		gtd->attach_sock = close(gtd->attach_sock);
+
+		show_message(gtd->ses, LIST_COMMAND, "#DAEMON INPUT: WRITE ERROR: UNATTACHING.");
+	}
+}
+
+DO_DAEMON(daemon_kill)
+{
+	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], filename[BUFFER_SIZE], sock_file[BUFFER_SIZE];
+	struct dirent **dirlist;
+	int size, index, pid;
+
+	sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (!get_daemon_dir(ses, filename))
+	{
+		return;
+	}
+
+	size = scandir(filename, &dirlist, 0, alphasort);
+
+	if (size == -1)
+	{
+		syserr_printf(ses, "do_attach: scandir:");
+
+		return;
+	}
+
+	for (*arg2 = index = pid = 0 ; index < size ; index++)
+	{
+		if (strlen(dirlist[index]->d_name) > 2)
+		{
+			tintin_printf2(ses, "#DAEMON KILL: CHECKING FILE {%s}", dirlist[index]->d_name);
+
+			if (*arg1)
+			{
+				if (!strstr(dirlist[index]->d_name, arg1))
+				{
+					continue;
+				}
+			}
+			arg = strchr(dirlist[index]->d_name, '.');
+
+			if (arg)
+			{
+				*arg = 0;
+
+				strcpy(arg2, dirlist[index]->d_name);
+
+				arg = strchr(dirlist[index]->d_name, '_');
+
+				if (arg)
+				{
+					pid = atoi(arg + 1);
+
+					sprintf(sock_file, "%s/%s.s", filename, arg2);
+
+					show_message(ses, LIST_COMMAND, "#DAEMON {%s} KILLED.", sock_file, pid);
+
+					kill((pid_t) pid, SIGKILL);
+
+					remove(sock_file);
+				}
+			}
+		}
+	}
+
+	for (index = 0 ; index < size ; index++)
+	{
+		free(dirlist[index]);
+	}
+	free(dirlist);
+
+	return;
+}
+
+DO_DAEMON(daemon_list)
+{
+	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], filename[BUFFER_SIZE], sock_file[BUFFER_SIZE];
+	struct dirent **dirlist;
+	int size, index, pid;
+
+	sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (!get_daemon_dir(ses, filename))
+	{
+		return;
+	}
+
+	size = scandir(filename, &dirlist, 0, alphasort);
+
+	if (size == -1)
+	{
+		syserr_printf(ses, "do_attach: scandir:");
+
+		return;
+	}
+
+	tintin_printf2(ses, "#THESE DAEMONS HAVE BEEN DEFINED:");
+
+	for (*arg2 = index = pid = 0 ; index < size ; index++)
+	{
+		if (strlen(dirlist[index]->d_name) > 2)
+		{
+			if (*arg1)
+			{
+				if (!strstr(dirlist[index]->d_name, arg1))
+				{
+					continue;
+				}
+			}
+			arg = strchr(dirlist[index]->d_name, '.');
+
+			if (arg)
+			{
+				*arg = 0;
+
+				strcpy(arg2, dirlist[index]->d_name);
+
+				arg = strchr(dirlist[index]->d_name, '_');
+
+				if (arg)
+				{
+					pid = atoi(arg + 1);
+
+					sprintf(sock_file, "%s/%s.s", filename, arg2);
+
+					tintin_printf2(ses, "%-40s [%6d]", sock_file, pid);
+				}
+			}
+		}
+	}
+	for (index = 0 ; index < size ; index++)
+	{
+		free(dirlist[index]);
+	}
+	free(dirlist);
+
+	return;
+}
+
+int get_daemon_dir(struct session *ses, char *filename)
+{
+	sprintf(filename, "%s/%s", gtd->home, TINTIN_DIR);
+
+	if (mkdir(filename, 0755) && errno != EEXIST)
+	{
+		show_error(ses, LIST_COMMAND, "#DAEMON CHECK DIR: FAILED TO CREATE TINTIN DIR %s (%s)", filename, strerror(errno));
+
+		return 0;
+	}
+
+	sprintf(filename, "%s/%s/%s", gtd->home, TINTIN_DIR, DAEMON_DIR);
+
+	if (mkdir(filename, 0755) && errno != EEXIST)
+	{
+		show_error(ses, LIST_COMMAND, "#DAEMON CHECK DIR: CANNOT CREATE DAEMON DIR %s (%s)", filename, strerror(errno));
+
+		return 0;
+	}
+	return 1;
+}
+
+void reset_daemon()
+{
+	if (gtd->detach_sock > 0)
+	{
+		print_stdout("removing(%s)\n", gtd->detach_file);
+
+		remove(gtd->detach_file);
+	}
+}
+
+void winch_daemon()
+{
+	if (gtd->attach_sock)
+	{
+		kill((pid_t) gtd->attach_pid, SIGWINCH);
+	}
+}

+ 52 - 16
src/data.c

@@ -83,6 +83,7 @@ struct listroot *copy_list(struct session *ses, struct listroot *sourcelist, int
 			node->arg2  = strdup(sourcelist->list[i]->arg2);
 			node->arg3  = strdup(sourcelist->list[i]->arg3);
 			node->arg4  = strdup(sourcelist->list[i]->arg4);
+			node->flags = sourcelist->list[i]->flags;
 			node->group = strdup(sourcelist->list[i]->group);
 
 			switch (type)
@@ -99,6 +100,13 @@ struct listroot *copy_list(struct session *ses, struct listroot *sourcelist, int
 					node->regex = tintin_regexp_compile(ses, node, node->arg1, 0);
 					break;
 
+				case LIST_BUTTON:
+					node->val16[0] = sourcelist->list[i]->val16[0];
+					node->val16[1] = sourcelist->list[i]->val16[1];
+					node->val16[2] = sourcelist->list[i]->val16[2];
+					node->val16[3] = sourcelist->list[i]->val16[3];
+					break;
+
 				case LIST_VARIABLE:
 					copy_nest_node(ses->list[type], node, sourcelist->list[i]);
 					break;
@@ -131,6 +139,11 @@ struct listnode *insert_node_list(struct listroot *root, char *arg1, char *arg2,
 	node->arg3 = strdup(arg3);
 	node->arg4 = strdup(arg4);
 
+	if (gtd->level->oneshot)
+	{
+		SET_BIT(node->flags, NODE_FLAG_ONESHOT);
+	}
+
 	node->group = HAS_BIT(root->flags, LIST_FLAG_CLASS) ? strdup(root->ses->group) : strdup("");
 
 	switch (root->type)
@@ -170,6 +183,11 @@ struct listnode *update_node_list(struct listroot *root, char *arg1, char *arg2,
 
 		node = root->list[index];
 
+		if (gtd->level->oneshot)
+		{
+			SET_BIT(node->flags, NODE_FLAG_ONESHOT);
+		}
+
 		if (strcmp(node->arg2, arg2) != 0)
 		{
 			free(node->arg2);
@@ -570,7 +588,7 @@ int show_node_with_wild(struct session *ses, char *text, struct listroot *root)
 			case LIST_EVENT:
 			case LIST_FUNCTION:
 			case LIST_MACRO:
-				tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2));
+				tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2));
 				break;
 
 			case LIST_ACTION:
@@ -651,6 +669,15 @@ void delete_node_with_wild(struct session *ses, int type, char *text)
 }
 
 
+DO_COMMAND(do_killall)
+{
+	tintin_printf2(ses, "\e[1;31m#NOTICE: PLEASE CHANGE #KILLALL TO #KILL ALL.");
+
+	do_kill(ses, arg);
+
+	return ses;
+}
+
 DO_COMMAND(do_kill)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
@@ -932,7 +959,6 @@ DO_COMMAND(do_info)
 					HAS_BIT(ses->list[index]->flags, LIST_FLAG_DEBUG)   ?  "ON" : "OFF",
 					HAS_BIT(ses->list[index]->flags, LIST_FLAG_LOG)     ? "LOG" : "   ");
 			}
-
 		}
 		tintin_header(ses, "");
 	}
@@ -970,7 +996,7 @@ DO_COMMAND(do_info)
 				{
 					for (cnt = 0 ; cnt < root->used ; cnt++)
 					{
-						tintin_printf2(ses, "#INFO %s %4d {arg1}{%s} {arg2}{%s} {arg3}{%s} {class}{%s}", list_table[index].name, cnt, root->list[cnt]->arg1, root->list[cnt]->arg2, root->list[cnt]->arg3, root->list[cnt]->group);
+						tintin_printf2(ses, "#INFO %s %4d {arg1}{%s} {arg2}{%s} {arg3}{%s} {arg4}{%s} {class}{%s} {flags}{%d}", list_table[index].name, cnt, root->list[cnt]->arg1, root->list[cnt]->arg2, root->list[cnt]->arg3, root->list[cnt]->arg4, root->list[cnt]->group, root->list[cnt]->flags);
 					}
 				}
 				else if (is_abbrev(arg2, "SAVE"))
@@ -982,20 +1008,14 @@ DO_COMMAND(do_info)
 					{
 						sprintf(name, "info[%s][%d]", list_table[index].name, cnt);
 
-						set_nest_node(ses->list[LIST_VARIABLE], name, "{arg1}{%s}{arg2}{%s}{arg3}{%s}{class}{%s}", root->list[cnt]->arg1, root->list[cnt]->arg2, root->list[cnt]->arg3, root->list[cnt]->group);
+						set_nest_node(ses->list[LIST_VARIABLE], name, "{arg1}{%s}{arg2}{%s}{arg3}{%s}{arg4}{%s}{class}{%s}{flags}{%d}", root->list[cnt]->arg1, root->list[cnt]->arg2, root->list[cnt]->arg3, root->list[cnt]->arg4, root->list[cnt]->group, root->list[cnt]->flags);
 					}
 					show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[%s]}", list_table[index].name);
 				}
 				else
 				{
 					show_error(ses, LIST_COMMAND, "#SYNTAX: #INFO {%s} [ON|OFF|LIST|SAVE|SYSTEM]", arg1);
-					
-					return ses;
 				}
-				found = TRUE;
-
-				show_error(ses, LIST_COMMAND, "#SYNTAX: #INFO {%s} [ON|OFF]", arg1);
-				
 				return ses;
 			}
 			show_message(ses, LIST_COMMAND, "#OK: #%s INFO STATUS HAS BEEN SET TO: %s.", list_table[index].name, HAS_BIT(ses->list[index]->flags, LIST_FLAG_INFO) ? "ON" : "OFF");
@@ -1009,6 +1029,17 @@ DO_COMMAND(do_info)
 			{
 				show_cpu(ses);
 			}
+			else if (is_abbrev(arg1, "MCCP"))
+			{
+				if (ses->mccp2)
+				{
+					tintin_printf2(ses, "#INFO MCCP2: TOTAL IN: %9ull TOTAL OUT: %9ull RATIO: %3d", ses->mccp2->total_in, ses->mccp2->total_out, 100 * ses->mccp2->total_out / ses->mccp2->total_in);
+				}
+				if (ses->mccp3)
+				{
+					tintin_printf2(ses, "#INFO MCCP3: TOTAL IN: %9ull TOTAL OUT: %9ull RATIO: %3d", ses->mccp3->total_in, ses->mccp3->total_out, 100 * ses->mccp3->total_out / ses->mccp3->total_in);
+				}
+			}
 			else if (is_abbrev(arg1, "STACK"))
 			{
 				dump_stack();
@@ -1023,18 +1054,23 @@ DO_COMMAND(do_info)
 
 					add_nest_node(ses->list[LIST_VARIABLE], name, "{CLIENT}{{NAME}{%s}{VERSION}{%-3s}}", CLIENT_NAME, CLIENT_VERSION);
 
-					add_nest_node(ses->list[LIST_VARIABLE], name, "{HOME}{%s}{LANG}{%s}{OS}{%s}{TERM}{%s}", gtd->home, gtd->lang, gtd->os, gtd->term);
+					add_nest_node(ses->list[LIST_VARIABLE], name, "{EXEC}{%s}{HOME}{%s}{LANG}{%s}{OS}{%s}{TERM}{%s}", gtd->exec, gtd->home, gtd->lang, gtd->os, gtd->term);
+
+					set_nest_node(ses->list[LIST_VARIABLE], name, "{DETACH_FILE}{%s}{ATTACH_FILE}{%-3s}", gtd->detach_port ? gtd->detach_file : "", gtd->attach_pid  ? gtd->attach_file : "");
 
 					show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[SYSTEM]}");
 				}
 				else
 				{
-					tintin_printf2(ses, "#INFO SYSTEM: CLIENT_NAME = %s", CLIENT_NAME);
+					tintin_printf2(ses, "#INFO SYSTEM: CLIENT_NAME    = %s", CLIENT_NAME);
 					tintin_printf2(ses, "#INFO SYSTEM: CLIENT_VERSION = %s", CLIENT_VERSION);
-					tintin_printf2(ses, "#INFO SYSTEM: HOME = %s", gtd->home);
-					tintin_printf2(ses, "#INFO SYSTEM: LANG = %s", gtd->lang);
-					tintin_printf2(ses, "#INFO SYSTEM: OS = %s", gtd->os);
-					tintin_printf2(ses, "#INFO SYSTEM: TERM = %s", gtd->term);
+					tintin_printf2(ses, "#INFO SYSTEM: EXEC           = %s", gtd->exec);
+					tintin_printf2(ses, "#INFO SYSTEM: HOME           = %s", gtd->home);
+					tintin_printf2(ses, "#INFO SYSTEM: LANG           = %s", gtd->lang);
+					tintin_printf2(ses, "#INFO SYSTEM: OS             = %s", gtd->os);
+					tintin_printf2(ses, "#INFO SYSTEM: TERM           = %s", gtd->term);
+					tintin_printf2(ses, "#INFO SYSTEM: DETACH_FILE    = %s", gtd->detach_port ? gtd->detach_file : "");
+					tintin_printf2(ses, "#INFO SYSTEM: ATTACH_FILE    = %s", gtd->attach_pid  ? gtd->attach_file : "");
 				}
 			}
 			else

+ 3 - 3
src/debug.c

@@ -27,12 +27,12 @@
 
 #include "tintin.h"
 
-#define MAX_STACK_SIZE     51
+#define MAX_STACK_SIZE     100
 #define MAX_DEBUG_SIZE     400
 
 char debug_stack[MAX_STACK_SIZE][MAX_DEBUG_SIZE];
 
-short debug_index;
+int debug_index = 0;
 
 int push_call(char *f, ...)
 {
@@ -96,7 +96,7 @@ void dump_stack_fatal(void)
 
 	for (i = 0 ; i < debug_index && i < MAX_STACK_SIZE ; i++)
 	{
-		printf("\e[1;32mDEBUG_STACK[\e[1;31m%03d\e[1;32m] = \e[1;31m%s\e[0m\n", i, debug_stack[i]);
+		print_stdout("\e[1;32mDEBUG_STACK[\e[1;31m%03d\e[1;32m] = \e[1;31m%s\e[0m\n", i, debug_stack[i]);
 	}
 }
 

+ 758 - 263
src/draw.c

@@ -24,16 +24,24 @@
 *                     coded by Igor van den Hoven 2019                        *
 ******************************************************************************/
 
+static char draw_buf[100][10];
+static int  draw_cnt;
+
 #include "tintin.h"
 
 DO_COMMAND(do_draw)
 {
-	char *nst_arg;
-	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE], arg4[BUFFER_SIZE];
+	char *sub_arg, arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], color[BUFFER_SIZE], arg3[BUFFER_SIZE], input[BUFFER_SIZE];
 	int index, flags;
 	int top_row, top_col, bot_row, bot_col, rows, cols;
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	substitute(ses, arg, input, SUB_VAR|SUB_FUN);
+
+	arg = input;
+
+	arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
+
+	draw_cnt = 0;
 
 	if (*arg1 == 0)
 	{
@@ -50,75 +58,197 @@ DO_COMMAND(do_draw)
 	}
 	else
 	{
-		flags = HAS_BIT(ses->flags, SES_FLAG_UTF8) ? DRAW_FLAG_UTF8 : 0;
-
-		nst_arg = get_arg_in_braces(ses, arg1, arg2, GET_ONE);
+		flags = HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) ? DRAW_FLAG_UTF8 : 0;
 
-/*		if (is_abbrev(arg2, "ESCAPED"))
+		while (*arg)
 		{
-			SET_BIT(flags, DRAW_FLAG_ESCAPED);
-		}
-		else */if (is_abbrev(arg2, "PRUNED"))
-		{
-			SET_BIT(flags, DRAW_FLAG_PRUNED);
-		}
-		else if (is_abbrev(arg2, "ROUNDED"))
-		{
-			SET_BIT(flags, DRAW_FLAG_ROUNDED);
-		}
-		else if (is_abbrev(arg2, "CROSSED"))
-		{
-			SET_BIT(flags, DRAW_FLAG_CROSSED);
-		}
-
-		if (HAS_BIT(flags, DRAW_FLAG_PRUNED|DRAW_FLAG_ROUNDED|DRAW_FLAG_CROSSED))
-		{
-			if (*nst_arg)
+			if (!HAS_BIT(flags, DRAW_FLAG_COLOR) && get_color_names(ses, arg1, color))
+			{
+				SET_BIT(flags, DRAW_FLAG_COLOR);
+			}
+			else if (!HAS_BIT(flags, DRAW_FLAG_COLOR) && strip_vt102_strlen(ses, arg1) == 0)
+			{
+				strcpy(color, arg1);
+				SET_BIT(flags, DRAW_FLAG_COLOR);
+			}
+			else if (is_abbrev(arg1, "ASCII"))
+			{
+				DEL_BIT(flags, DRAW_FLAG_UTF8);
+			}
+			else if (is_abbrev(arg1, "BLANKED"))
+			{
+				SET_BIT(flags, DRAW_FLAG_BLANKED|DRAW_FLAG_BOXED);
+			}
+			else if (is_abbrev(arg1, "BOTTOM"))
+			{
+				SET_BIT(flags, DRAW_FLAG_BOT);
+			}
+			else if (is_abbrev(arg1, "BUMPED"))
 			{
-				get_arg_in_braces(ses, nst_arg, arg1, GET_ALL);
+				SET_BIT(flags, DRAW_FLAG_BUMP);
+			}
+			else if (is_abbrev(arg1, "CIRCLED"))
+			{
+				SET_BIT(flags, DRAW_FLAG_CIRCLED);
+			}
+			else if (is_abbrev(arg1, "CONVERT"))
+			{
+				SET_BIT(flags, DRAW_FLAG_CONVERT);
+			}
+			else if (is_abbrev(arg1, "CORNERED"))
+			{
+				SET_BIT(flags, DRAW_FLAG_CORNERED);
+			}
+			else if (is_abbrev(arg1, "CROSSED"))
+			{
+				SET_BIT(flags, DRAW_FLAG_CROSSED|DRAW_FLAG_BOXED);
+			}
+			else if (is_abbrev(arg1, "FILLED"))
+			{
+				SET_BIT(flags, DRAW_FLAG_FILLED);
+			}
+			else if (is_abbrev(arg1, "HORIZONTAL"))
+			{
+				SET_BIT(flags, DRAW_FLAG_HOR);
+			}
+			else if (is_abbrev(arg1, "JEWELED"))
+			{
+				SET_BIT(flags, DRAW_FLAG_JEWELED);
+			}
+			else if (is_abbrev(arg1, "LEFT"))
+			{
+				SET_BIT(flags, DRAW_FLAG_LEFT);
+			}
+			else if (is_abbrev(arg1, "NUMBERED"))
+			{
+				SET_BIT(flags, DRAW_FLAG_NUMBERED);
+			}
+			else if (is_abbrev(arg1, "PRUNED"))
+			{
+				SET_BIT(flags, DRAW_FLAG_PRUNED|DRAW_FLAG_BOXED);
+			}
+			else if (is_abbrev(arg1, "RIGHT"))
+			{
+				SET_BIT(flags, DRAW_FLAG_RIGHT);
+			}
+			else if (is_abbrev(arg1, "ROUNDED"))
+			{
+				SET_BIT(flags, DRAW_FLAG_ROUNDED|DRAW_FLAG_BOXED);
+			}
+			else if (is_abbrev(arg1, "TEED"))
+			{
+				SET_BIT(flags, DRAW_FLAG_TEED|DRAW_FLAG_BOXED);
+			}
+			else if (is_abbrev(arg1, "TOP"))
+			{
+				SET_BIT(flags, DRAW_FLAG_TOP);
+			}
+			else if (is_abbrev(arg1, "TUBED"))
+			{
+				SET_BIT(flags, DRAW_FLAG_TUBED);
+			}
+			else if (is_abbrev(arg1, "UNICODE"))
+			{
+				SET_BIT(flags, DRAW_FLAG_UTF8);
+			}
+			else if (is_abbrev(arg1, "VERTICAL"))
+			{
+				SET_BIT(flags, DRAW_FLAG_VER);
 			}
 			else
 			{
-				arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
+				break;
 			}
+			arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
 		}
 
 		for (index = 0 ; *draw_table[index].name ; index++)
 		{
 			if (is_abbrev(arg1, draw_table[index].name))
 			{
-				arg = sub_arg_in_braces(ses, arg, arg4, GET_ALL, SUB_VAR|SUB_FUN);
+				arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 
-				nst_arg = arg4;
+				sub_arg = get_arg_in_braces(ses, arg1, arg3, GET_ONE);
 
-				nst_arg = get_arg_in_braces(ses, nst_arg, arg2, GET_ONE);
-				nst_arg = get_arg_in_braces(ses, nst_arg, arg3, GET_ONE);
+				if (*sub_arg)
+				{
+					strcpy(arg1, arg3);
+					sub_arg = get_arg_in_braces(ses, sub_arg, arg2, GET_ONE);
+				}
+				else
+				{
+					arg = get_arg_in_braces(ses, arg, arg2, GET_ONE);
+				}
 
-				top_row = get_row_index(ses, arg2);
-				top_col = get_col_index(ses, arg3);
+				top_row = get_row_index(ses, arg1);
+				top_col = get_col_index(ses, arg2);
 
-				nst_arg = get_arg_in_braces(ses, nst_arg, arg2, GET_ONE);
-				nst_arg = get_arg_in_braces(ses, nst_arg, arg3, GET_ONE);
+				if (*sub_arg)
+				{
+					sub_arg = get_arg_in_braces(ses, sub_arg, arg1, GET_ONE);
+					sub_arg = get_arg_in_braces(ses, sub_arg, arg2, GET_ONE);
+				}
+				else
+				{
+					arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
+					arg = get_arg_in_braces(ses, arg, arg2, GET_ONE);
+				}
+				bot_row = get_row_index(ses, arg1);
+				bot_col = get_col_index(ses, arg2);
 
-				bot_row = get_row_index(ses, arg2);
-				bot_col = get_col_index(ses, arg3);
+				if (top_row == 0 && top_col == 0)
+				{
+					show_error(ses, LIST_COMMAND, "#SYNTAX: #DRAW [COLOR] [OPTIONS] {%s} <TOP_ROW> <TOP_COL> <BOT_ROW> <BOT_COL> [TEXT]", draw_table[index].name);
 
-				rows = URANGE(1, 1 + bot_row - top_row, gtd->screen->rows);
-				cols = URANGE(1, 1 + bot_col - top_col, gtd->screen->cols);
+					return ses;
+				}
 
-				if (*arg == 0)
+				if (top_row == 0)
 				{
-					arg = nst_arg;
+					top_row = 1;
+
+					SET_BIT(flags, DRAW_FLAG_SCROLL);
 				}
 
+				if (top_col == 0)
+				{
+					top_col = 1;
+				}
+
+				if (bot_row == 0)
+				{
+					bot_row = 1;
+				}
+
+				if (bot_col == 0)
+				{
+					bot_col = 1;
+				}
+
+				rows = URANGE(1, 1 + bot_row - top_row, gtd->screen->rows);
+				cols = URANGE(1, 1 + bot_col - top_col, gtd->screen->cols);
+
 				*arg1 = 0;
 				*arg2 = 0;
+				*arg3 = 0;
+
+				if (*arg == 0)
+				{
+					arg = arg3;
+				}
 
 				save_pos(ses);
 
-				draw_table[index].fun(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, arg, arg1, arg2);
+				if (HAS_BIT(flags, DRAW_FLAG_BUMP))
+				{
+					tintin_printf2(ses, "");
+				}
+
+				draw_table[index].fun(ses, top_row, top_col, bot_row, bot_col, rows, cols, draw_table[index].flags | flags, color, arg, arg1, arg2);
+
+				print_stdout("\e[0m");
 
-				load_pos(ses);
+				restore_pos(ses);
 
 				return ses;
 			}
@@ -133,211 +263,469 @@ DO_COMMAND(do_draw)
 
 char *draw_corner(int flags, char *str)
 {
-	static char result[10][10];
-	static int cnt;
+	draw_cnt = (draw_cnt + 1) % 100;
 
-	cnt = (cnt + 1) % 10;
-
-	if (HAS_BIT(flags, DRAW_FLAG_UTF8))
+	if (HAS_BIT(flags, DRAW_FLAG_BLANKED))
+	{
+		strcpy(draw_buf[draw_cnt], " ");
+	}
+	else if (HAS_BIT(flags, DRAW_FLAG_NUMBERED))
+	{
+		sprintf(draw_buf[draw_cnt], "%d", draw_cnt % 10);
+	}
+	else if (HAS_BIT(flags, DRAW_FLAG_UTF8))
 	{
 		if (HAS_BIT(flags, DRAW_FLAG_PRUNED))
 		{
-			strcpy(result[cnt], "\e[C");
+			if (HAS_BIT(flags, DRAW_FLAG_SCROLL))
+			{
+				strcpy(draw_buf[draw_cnt], " ");
+			}
+			else
+			{
+				strcpy(draw_buf[draw_cnt], "\e[C");
+			}
 		}
-		else if (HAS_BIT(flags, DRAW_FLAG_ROUNDED))
+		else if (HAS_BIT(flags, DRAW_FLAG_CIRCLED))
 		{
-			strcpy(result[cnt], "○");
+			if (HAS_BIT(flags, DRAW_FLAG_CROSSED))
+			{
+				strcpy(draw_buf[draw_cnt], "ϴ");
+			}
+			else if (HAS_BIT(flags, DRAW_FLAG_FILLED))
+			{
+				strcpy(draw_buf[draw_cnt], "⬤");
+			}
+			else if (HAS_BIT(flags, DRAW_FLAG_VER))
+			{
+				strcpy(draw_buf[draw_cnt], "O");
+			}
+			else
+			{
+				strcpy(draw_buf[draw_cnt], "○");
+			}
 		}
 		else if (HAS_BIT(flags, DRAW_FLAG_CROSSED))
 		{
-			strcpy(result[cnt], "┼");
+			strcpy(draw_buf[draw_cnt], "┼");
+		}
+		else if (HAS_BIT(flags, DRAW_FLAG_JEWELED))
+		{
+			if (HAS_BIT(flags, DRAW_FLAG_FILLED))
+			{
+				strcpy(draw_buf[draw_cnt], "⧫");
+			}
+			else
+			{
+				strcpy(draw_buf[draw_cnt], "◊");
+			}
+		}
+		else if (HAS_BIT(flags, DRAW_FLAG_TEED))
+		{
+			if (HAS_BIT(flags, DRAW_FLAG_HOR))
+			{
+				if (HAS_BIT(flags, DRAW_FLAG_LEFT))
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+					{
+						strcpy(draw_buf[draw_cnt], "╠");
+					}
+					else
+					{
+						strcpy(draw_buf[draw_cnt], "├");
+					}
+				}
+				else
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+					{
+						strcpy(draw_buf[draw_cnt], "╣");
+					}
+					else
+					{
+						strcpy(draw_buf[draw_cnt], "┤");
+					}
+				}
+			}
+			else
+			{
+				if (HAS_BIT(flags, DRAW_FLAG_TOP))
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+					{
+						strcpy(draw_buf[draw_cnt], "╦");
+					}
+					else
+					{
+						strcpy(draw_buf[draw_cnt], "┬");
+					}
+				}
+				else
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+					{
+						strcpy(draw_buf[draw_cnt], "╧");
+					}
+					else
+					{
+						strcpy(draw_buf[draw_cnt], "┴");
+					}
+				}
+			}
+		}
+		else if (HAS_BIT(flags, DRAW_FLAG_BOXED) || HAS_BIT(flags, DRAW_FLAG_CORNERED))
+		{
+			if (HAS_BIT(flags, DRAW_FLAG_LEFT))
+			{
+				if (HAS_BIT(flags, DRAW_FLAG_TOP))
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_ROUNDED))
+					{
+						strcpy(draw_buf[draw_cnt], "╭");
+					}
+					else
+					{
+						if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+						{
+							strcpy(draw_buf[draw_cnt], "╔");
+						}
+						else
+						{
+							strcpy(draw_buf[draw_cnt], "┌");
+						}
+					}
+				}
+				else if (HAS_BIT(flags, DRAW_FLAG_BOT))
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_ROUNDED))
+					{
+						strcpy(draw_buf[draw_cnt], "╰");
+					}
+					else
+					{
+						if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+						{
+							strcpy(draw_buf[draw_cnt], "╚");
+						}
+						else
+						{
+							strcpy(draw_buf[draw_cnt], "└");
+						}
+					}
+				}
+				else
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_HOR))
+					{
+						if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+						{
+							strcpy(draw_buf[draw_cnt], "═");
+						}
+						else
+						{
+							strcpy(draw_buf[draw_cnt], "─");
+						}
+					}
+					else
+					{
+						if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+						{
+							strcpy(draw_buf[draw_cnt], "║");
+						}
+						else
+						{
+							strcpy(draw_buf[draw_cnt], "│");
+						}
+					}
+				}
+			}
+			else if (HAS_BIT(flags, DRAW_FLAG_RIGHT))
+			{
+				if (HAS_BIT(flags, DRAW_FLAG_TOP))
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_ROUNDED))
+					{
+						strcpy(draw_buf[draw_cnt], "╮");
+					}
+					else
+					{
+						if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+						{
+							strcpy(draw_buf[draw_cnt], "╗");
+						}
+						else
+						{
+							strcpy(draw_buf[draw_cnt], "┐");
+						}
+					}
+				}
+				else if (HAS_BIT(flags, DRAW_FLAG_BOT))
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_ROUNDED))
+					{
+						strcpy(draw_buf[draw_cnt], "╯");
+					}
+					else
+					{
+						if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+						{
+							strcpy(draw_buf[draw_cnt], "╝");
+						}
+						else
+						{
+							strcpy(draw_buf[draw_cnt], "┘");
+						}
+					}
+				}
+				else
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_HOR))
+					{
+						if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+						{
+							strcpy(draw_buf[draw_cnt], "═");
+						}
+						else
+						{
+							strcpy(draw_buf[draw_cnt], "─");
+						}
+					}
+					else
+					{
+						if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+						{
+							strcpy(draw_buf[draw_cnt], "║");
+						}
+						else
+						{
+							strcpy(draw_buf[draw_cnt], "│");
+						}
+					}
+				}
+			}
+			else
+			{
+				if (HAS_BIT(flags, DRAW_FLAG_HOR))
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+					{
+						strcpy(draw_buf[draw_cnt], "═");
+					}
+					else
+					{
+						strcpy(draw_buf[draw_cnt], "─");
+					}
+				}
+				else
+				{
+					if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+					{
+						strcpy(draw_buf[draw_cnt], "║");
+					}
+					else
+					{
+						strcpy(draw_buf[draw_cnt], "│");
+					}
+				}
+			}
 		}
 		else
 		{
-			strcpy(result[cnt], str);
+			if (HAS_BIT(flags, DRAW_FLAG_HOR))
+			{
+				if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+				{
+					strcpy(draw_buf[draw_cnt], "═");
+				}
+				else
+				{
+					strcpy(draw_buf[draw_cnt], "─");
+				}
+			}
+			else if (HAS_BIT(flags, DRAW_FLAG_VER))
+			{
+				if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+				{
+					strcpy(draw_buf[draw_cnt], "║");
+				}
+				else
+				{
+					strcpy(draw_buf[draw_cnt], "│");
+				}
+			}
+			else
+			{
+				strcpy(draw_buf[draw_cnt], "?");
+			}
 		}
 	}
 	else
 	{
 		if (HAS_BIT(flags, DRAW_FLAG_PRUNED))
 		{
-			strcpy(result[cnt], "\e[C");
+			strcpy(draw_buf[draw_cnt], "\e[C");
 		}
-		else if (HAS_BIT(flags, DRAW_FLAG_ROUNDED))
+		else if (HAS_BIT(flags, DRAW_FLAG_CIRCLED) || HAS_BIT(flags, DRAW_FLAG_ROUNDED))
 		{
-			strcpy(result[cnt], "o");
+			strcpy(draw_buf[draw_cnt], "o");
 		}
 		else if (HAS_BIT(flags, DRAW_FLAG_CROSSED))
 		{
-			strcpy(result[cnt], "+");
+			strcpy(draw_buf[draw_cnt], "+");
 		}
 		else
 		{
-			strcpy(result[cnt], "+");
+			strcpy(draw_buf[draw_cnt], "+");
 		}
 	}
-	return result[cnt];
+	return draw_buf[draw_cnt];
 }
 
 char *draw_horizontal(int flags, char *str)
 {
-	static char result[10][10];
-	static int cnt;
+	draw_cnt = (draw_cnt + 1) % 100;
 
-	cnt = (cnt + 1) % 10;
-
-	if (HAS_BIT(flags, DRAW_FLAG_UTF8))
+	if (HAS_BIT(flags, DRAW_FLAG_BLANKED))
+	{
+		strcpy(draw_buf[draw_cnt], " ");
+	}
+	else if (HAS_BIT(flags, DRAW_FLAG_NUMBERED))
 	{
-		strcpy(result[cnt], "─");
+		sprintf(draw_buf[draw_cnt], "%d", draw_cnt % 10);
+	}
+	else if (HAS_BIT(flags, DRAW_FLAG_UTF8))
+	{
+		if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+		{
+			strcpy(draw_buf[draw_cnt], "═");
+		}
+		else
+		{
+			strcpy(draw_buf[draw_cnt], "─");
+		}
 	}
 	else
 	{
-		strcpy(result[cnt], "-");
+		strcpy(draw_buf[draw_cnt], "-");
 	}
-	return result[cnt];
+	return draw_buf[draw_cnt];
 }
 
 char *draw_vertical(int flags, char *str)
 {
-	static char result[10][10];
-	static int cnt;
-
-	cnt = (cnt + 1) % 10;
+	draw_cnt = (draw_cnt + 1) % 100;
 
-	if (HAS_BIT(flags, DRAW_FLAG_UTF8))
+	if (HAS_BIT(flags, DRAW_FLAG_BLANKED))
 	{
-		strcpy(result[cnt], "│");
+		strcpy(draw_buf[draw_cnt], " ");
 	}
-	else
+	else if (HAS_BIT(flags, DRAW_FLAG_NUMBERED))
 	{
-		strcpy(result[cnt], "|");
+		sprintf(draw_buf[draw_cnt], "%d", draw_cnt % 10);
 	}
-	return result[cnt];
-}
-
-// subcommands
-
-DO_DRAW(draw_blank)
-{
-	int row;
-
-	arg = arg1;
-
-	arg1 += sprintf(arg1, "\e[%d;%dH", top_row, top_col);
-
-	for (row = 0 ; row < rows ; row++)
+	else if (HAS_BIT(flags, DRAW_FLAG_UTF8))
 	{
-		arg1 += sprintf(arg1, "\e[%dX\v", bot_col - top_col + 1);
+		if (HAS_BIT(flags, DRAW_FLAG_TUBED))
+		{
+			strcpy(draw_buf[draw_cnt], "║");
+		}
+		else
+		{
+			strcpy(draw_buf[draw_cnt], "│");
+		}
 	}
-
-	printf("%s", arg);
-}
-
-
-DO_DRAW(draw_bot_line)
-{
-	int col;
-
-	goto_pos(ses, bot_row, top_col);
-
-	arg = arg1;
-
-	arg1 += sprintf(arg1, "%s", draw_corner(flags, "└"));
-
-	for (col = top_col + 1 ; col < bot_col ; col++)
+	else
 	{
-		arg1 += sprintf(arg1, "%s", draw_horizontal(flags, "─"));
+		strcpy(draw_buf[draw_cnt], "|");
 	}
-
-	arg1 += sprintf(arg1, "%s", draw_corner(flags, "┘"));
-
-	printf("%s", arg);
+	return draw_buf[draw_cnt];
 }
 
-DO_DRAW(draw_box)
-{
-	draw_top_line(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, arg, arg1, arg2);
-	draw_bot_line(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, arg, arg1, arg2);
-
-	draw_left_line (ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, arg, arg1, arg2);
-	draw_right_line(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, arg, arg1, arg2);
-}
+// options
 
-DO_DRAW(draw_box_text)
+DO_DRAW(draw_bot_side)
 {
-	draw_box (ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, arg, arg1, arg2);
+	int col, corner;
 
-	draw_text(ses, top_row + 1, top_col + 1, bot_row - 1, bot_col - 1, rows - 2, cols - 2, flags, arg, arg1, arg2);
-}
+	SET_BIT(flags, HAS_BIT(flags, DRAW_FLAG_VER) ? DRAW_FLAG_VER : DRAW_FLAG_HOR);
 
-DO_DRAW(draw_center_left_line)
-{
-	int row = top_row;
+	corner = flags;
 
-	arg = arg1;
+	DEL_BIT(corner, DRAW_FLAG_RIGHT|DRAW_FLAG_TOP);
 
-	arg1 += sprintf(arg1, "\e[%d;%dH%s", top_row, top_col, draw_corner(flags, "┬"));
+	arg = arg1;
 
-	while (++row < bot_row)
+	if (HAS_BIT(flags, DRAW_FLAG_LEFT) || HAS_BIT(flags, DRAW_FLAG_BOT))
 	{
-		arg1 += sprintf(arg1, "\e[%d;%dH%s", row, top_col, draw_vertical(flags, "│"));
+		arg1 += sprintf(arg1, "%s%s", color, draw_corner(corner, "└"));
 	}
 
-	arg1 += sprintf(arg1, "\e[%d;%dH%s", bot_row, top_col, draw_corner(flags, "┴"));
-
-	printf("%s", arg);
-}
+	if (cols - 2 >= 0)
+	{
+		if (HAS_BIT(flags, DRAW_FLAG_BOT))
+		{
+			for (col = top_col + 1 ; col < bot_col ; col++)
+			{
+				arg1 += sprintf(arg1, "%s", draw_horizontal(flags, "─"));
+			}
+		}
+		else if (HAS_BIT(flags, DRAW_FLAG_RIGHT) && cols - 2 > 0)
+		{
+			arg1 += sprintf(arg1, "\e[%dC", cols - 2);
+		}
 
-DO_DRAW(draw_center_right_line)
-{
-	int row = top_row;
+		corner = flags;
 
-	arg = arg1;
+		DEL_BIT(corner, DRAW_FLAG_LEFT|DRAW_FLAG_TOP);
 
-	arg1 += sprintf(arg1, "\e[%d;%dH%s", top_row, bot_col, draw_corner(flags, "┬"));
+		if (HAS_BIT(flags, DRAW_FLAG_RIGHT) || HAS_BIT(flags, DRAW_FLAG_BOT))
+		{
+			arg1 += sprintf(arg1, "%s", draw_corner(corner, "┘"));
+		}
+	}
 
-	while (++row < bot_row)
+	if (HAS_BIT(flags, DRAW_FLAG_SCROLL))
 	{
-		arg1 += sprintf(arg1, "\e[%d;%dH%s", row, bot_col, draw_vertical(flags, "│"));
+		tintin_printf2(ses, "%s%s", indent_one(top_col - 1), arg);
 	}
+	else
+	{
+		goto_pos(ses, bot_row, top_col);
 
-	arg1 += sprintf(arg1, "\e[%d;%dH%s", bot_row, bot_col, draw_corner(flags, "┴"));
-
-	printf("%s", arg);
+		print_stdout("%s", arg);
+	}
 }
 
-DO_DRAW(draw_horizontal_line)
+DO_DRAW(draw_box)
 {
-	int col;
-
-	arg = arg1;
-
-	arg1 += sprintf(arg1, "\e[%d;%dH%s", top_row, top_col, draw_horizontal(flags, "─"));
+	draw_top_side(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, color, arg, arg1, arg2);
 
-	for (col = top_col + 1 ; col <= bot_col ; col++)
-	{
-		arg1 += sprintf(arg1, "%s", draw_horizontal(flags, "─"));
-	}
+	draw_vertical_lines(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, color, arg, arg1, arg2);
 
-	printf("%s", arg);
+	draw_bot_side(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, color, arg, arg1, arg2);
 }
 
-DO_DRAW(draw_left_line)
+DO_DRAW(draw_line)
 {
-	int row = top_row;
-
-	arg = arg1;
-
-	arg1 += sprintf(arg1, "\e[%d;%dH%s", top_row, top_col, draw_corner(flags, "┌"));
-
-	while (++row < bot_row)
+	if (top_row == bot_row)
 	{
-		arg1 += sprintf(arg1, "\e[%d;%dH%s", row, top_col, draw_vertical(flags, "│"));
-	}
+		SET_BIT(flags, DRAW_FLAG_HOR|DRAW_FLAG_LEFT|DRAW_FLAG_RIGHT);
 
-	arg1 += sprintf(arg1, "\e[%d;%dH%s", bot_row, top_col, draw_corner(flags, "└"));
+		if (!HAS_BIT(flags, DRAW_FLAG_BOT))
+		{
+			SET_BIT(flags, DRAW_FLAG_TOP);
+		}
+	}
+	if (top_col == bot_col)
+	{
+		SET_BIT(flags, DRAW_FLAG_VER|DRAW_FLAG_TOP|DRAW_FLAG_BOT);
 
-	printf("%s", arg);
+		if (!HAS_BIT(flags, DRAW_FLAG_RIGHT))
+		{
+			SET_BIT(flags, DRAW_FLAG_LEFT);
+		}
+	}
+	draw_box(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, color, arg, arg1, arg2);
 }
 
 DO_DRAW(draw_map)
@@ -359,169 +747,276 @@ DO_DRAW(draw_map)
 
 	save_pos(ses);
 
-	draw_text(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, arg2, arg1, arg);
+	draw_text(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, color, arg2, arg1, arg);
 
-	load_pos(ses);
+	restore_pos(ses);
 }
 
-DO_DRAW(draw_middle_top_line)
+DO_DRAW(draw_side)
 {
-	int col;
+	push_call("draw_side()");
 
-	goto_pos(ses, top_row, top_col);
+	draw_box(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, color, arg, arg1, arg2);
 
-	arg = arg1;
+	pop_call();
+	return;
+}
 
-	arg1 += sprintf(arg1, "%s", draw_corner(flags, "├"));
+DO_DRAW(draw_square)
+{
+	draw_text(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, color, arg, arg1, arg2);
+}
+
+DO_DRAW(draw_text)
+{
+	char *txt, buf1[BUFFER_SIZE], buf2[BUFFER_SIZE], buf3[BUFFER_SIZE];
+	int row, col, height, width;
+
+	push_call("draw_text(%p,%d,%p,%p,%p)",ses,flags,arg,arg1,arg2);
 
-	for (col = top_col + 1 ; col < bot_col ; col++)
+	buf1[0] = buf2[0] = arg1[0] = arg2[0] = 0;
+
+	txt = buf2;
+
+	substitute(ses, arg, buf3, SUB_VAR|SUB_FUN);
+
+	arg = buf3;
+
+	while (*arg)
 	{
-		arg1 += sprintf(arg1, "%s", draw_horizontal(flags, "─"));
-	}
-	
-	arg1 += sprintf(arg1, "%s", draw_corner(flags, "┤"));
+		arg = sub_arg_in_braces(ses, arg, buf1, GET_ALL, SUB_COL|SUB_ESC|SUB_VAR|SUB_FUN);
 
-	printf("%s", arg);
-}
+		txt += sprintf(txt, "%s\n", buf1);
 
-DO_DRAW(draw_middle_bot_line)
-{
-	int col;
+		if (*arg == COMMAND_SEPARATOR)
+		{
+			arg++;
+		}
+	}
 
-	goto_pos(ses, bot_row, top_col);
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED) || HAS_BIT(flags, DRAW_FLAG_TOP) || HAS_BIT(flags, DRAW_FLAG_PRUNED))
+	{
+		top_row++;
+		rows--;
+	}
 
-	arg = arg1;
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED) || HAS_BIT(flags, DRAW_FLAG_BOT) || HAS_BIT(flags, DRAW_FLAG_PRUNED))
+	{
+		bot_row--;
+		rows--;
+	}
 
-	arg1 += sprintf(arg1, "%s", draw_corner(flags, "├"));
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED) || HAS_BIT(flags, DRAW_FLAG_LEFT) || HAS_BIT(flags, DRAW_FLAG_PRUNED))
+	{
+		strcpy(arg1, " ");
+		cols--;
+	}
 
-	for (col = top_col + 1 ; col < bot_col ; col++)
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED) || HAS_BIT(flags, DRAW_FLAG_RIGHT) || HAS_BIT(flags, DRAW_FLAG_PRUNED))
 	{
-		arg1 += sprintf(arg1, "%s", draw_horizontal(flags, "─"));
+		strcpy(arg2, " ");
+		cols--;
 	}
 
-	arg1 += sprintf(arg1, "%s", draw_corner(flags, "┤"));
+	word_wrap_split(ses, buf2, buf1, cols, 0, 0, FLAG_NONE, &height, &width);
 
-	printf("%s", arg);
-}
+	height--;
 
-DO_DRAW(draw_right_line)
-{
-	int row = top_row;
+	txt = buf1;
 
-	arg = arg1;
+	while (*txt && height > rows)
+	{
+		txt = strchr(txt, '\n');
+		txt++;
+		height--;
+	}
 
-	arg1 += sprintf(arg1, "\e[%d;%dH%s", top_row, bot_col, draw_corner(flags, "┐"));
+	arg = txt;
+	row = top_row;
+	col = top_col;
 
-	while (++row < bot_row)
+	while (*arg)
 	{
-		arg1 += sprintf(arg1, "\e[%d;%dH%s", row, bot_col, draw_vertical(flags, "│"));
+		arg = strchr(arg, '\n');
+
+		*arg++ = 0;
+
+		justify_string(ses, txt, buf2, 0 - cols, cols);
+
+		if (HAS_BIT(flags, DRAW_FLAG_LEFT))
+		{
+			strcpy(arg1, draw_vertical(flags, "│"));
+		}
+
+		if (HAS_BIT(flags, DRAW_FLAG_RIGHT))
+		{
+			strcpy(arg2, draw_vertical(flags, "│"));
+		}
+
+		if (HAS_BIT(flags, DRAW_FLAG_CONVERT))
+		{
+			convert_meta(buf2, buf3, FALSE);
+			strcpy(buf2, buf3);
+		}
+
+		if (HAS_BIT(flags, DRAW_FLAG_SCROLL))
+		{
+			tintin_printf2(ses, "%s%s%s%s%s%s", indent_one(top_col - 1), color, arg1, buf2, color, arg2);
+		}
+		else
+		{
+			goto_pos(ses, row++, col);
+
+			print_stdout("%s%s%s%s%s", color, arg1, buf2, color, arg2);
+		}
+
+		txt = arg;
 	}
 
-	arg1 += sprintf(arg1, "\e[%d;%dH%s", bot_row, bot_col, draw_corner(flags, "┘"));
+	while (height < rows)
+	{
+		if (HAS_BIT(flags, DRAW_FLAG_LEFT))
+		{
+			strcpy(arg1, draw_vertical(flags, "│"));
+		}
+
+		if (HAS_BIT(flags, DRAW_FLAG_RIGHT))
+		{
+			strcpy(arg2, draw_vertical(flags, "│"));
+		}
 
-	printf("%s", arg);
-}
+		if (HAS_BIT(flags, DRAW_FLAG_SCROLL))
+		{
+			
+			tintin_printf2(ses, "%s%s%s%-*s%s%s", indent_one(top_col - 1), color, arg1, cols, "", color, arg2);
+		}
+		else
+		{
+			goto_pos(ses, row++, col);
 
-DO_DRAW(draw_side_lines)
-{
-	draw_vertical_line(ses, top_row, top_col, bot_row, top_col, rows, cols, flags, arg, arg1, arg2);
-	draw_vertical_line(ses, top_row, bot_col, bot_row, bot_col, rows, cols, flags, arg, arg1, arg2);
+			print_stdout("%s%s%*s%s%s", color, arg1, cols, "", color, arg2);
+		}
+		height++;
+	}
+	pop_call();
+	return;
 }
 
-DO_DRAW(draw_side_lines_text)
+DO_DRAW(draw_top_side)
 {
-	draw_side_lines(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, arg, arg1, arg2);
+	int col, corner;
 
-	draw_text(ses, top_row, top_col + 1, bot_row, bot_col - 1, rows, cols - 2, flags, arg, arg1, arg2);
-}
+	SET_BIT(flags, HAS_BIT(flags, DRAW_FLAG_VER) ? DRAW_FLAG_VER : DRAW_FLAG_HOR);
 
-DO_DRAW(draw_top_line)
-{
-	int col;
+	corner = flags;
 
-	goto_pos(ses, top_row, top_col);
+	DEL_BIT(corner, DRAW_FLAG_RIGHT|DRAW_FLAG_BOT);
 
 	arg = arg1;
 
-	arg1 += sprintf(arg1, "%s", draw_corner(flags, "┌"));
+	if (HAS_BIT(flags, DRAW_FLAG_LEFT) || HAS_BIT(flags, DRAW_FLAG_TOP))
+	{
+		arg1 += sprintf(arg1, "%s%s", color, draw_corner(corner, "┌"));
+	}
 
-	for (col = top_col + 1 ; col < bot_col ; col++)
+	if (cols - 2 >= 0)
 	{
-		arg1 += sprintf(arg1, "%s", draw_horizontal(flags, "─"));
+		if (HAS_BIT(flags, DRAW_FLAG_TOP))
+		{
+			for (col = top_col + 1 ; col < bot_col ; col++)
+			{
+				arg1 += sprintf(arg1, "%s", draw_horizontal(flags, "─"));
+			}
+		}
+		else if (HAS_BIT(flags, DRAW_FLAG_RIGHT) && cols - 2 > 0)
+		{
+			arg1 += sprintf(arg1, "\e[%dC", cols - 2);
+		}
+
+		corner = flags;
+
+		DEL_BIT(corner, DRAW_FLAG_LEFT|DRAW_FLAG_BOT);
+
+		if (HAS_BIT(flags, DRAW_FLAG_TOP) || HAS_BIT(flags, DRAW_FLAG_RIGHT))
+		{
+			arg1 += sprintf(arg1, "%s", draw_corner(corner, "┐"));
+		}
 	}
-	
-	arg1 += sprintf(arg1, "%s", draw_corner(flags, "┐"));
 
-	printf("%s", arg);
-}
+	if (HAS_BIT(flags, DRAW_FLAG_SCROLL))
+	{
+		tintin_printf2(ses, "%*s%s", top_col - 1, "", arg);
+	}
+	else
+	{
+		goto_pos(ses, top_row, top_col);
 
+		print_stdout("%s", arg);
+	}
+}
 
-DO_DRAW(draw_text)
+DO_DRAW(draw_vertical_lines)
 {
-	char *txt;
 	int row, col, lines;
 
-	push_call("draw_text(%p,%d,%p,%p,%p)",ses,flags,arg,arg1,arg2);
+	push_call("draw_vertical_lines(%p,%d,%p,%p,%p)",ses,flags,arg,arg1,arg2);
 
-	draw_blank(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, arg, arg1, arg2);
-
-	txt = arg2;
-
-	while (*arg)
+	if (HAS_BIT(flags, DRAW_FLAG_SCROLL) || *arg)
 	{
-		arg = sub_arg_in_braces(ses, arg, arg1, SUB_VAR|SUB_FUN|SUB_COL|SUB_ESC, GET_ALL);
+		draw_text(ses, top_row, top_col, bot_row, top_col, rows, cols, flags, color, arg, arg1, arg2);
 
-		txt += sprintf(txt, "%s\n", arg1);
+		pop_call();
+		return;
+	}
 
-		if (*arg == COMMAND_SEPARATOR)
-		{
-			arg++;
-		}
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED) || HAS_BIT(flags, DRAW_FLAG_TOP))
+	{
+		top_row++;
+		rows--;
 	}
 
-	lines = -1 + word_wrap_split(ses, arg2, arg1, cols, 0, BUFFER_SIZE / cols);
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED) || HAS_BIT(flags, DRAW_FLAG_BOT))
+	{
+		bot_row--;
+		rows--;
+	}
 
-	txt = arg1;
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED) || HAS_BIT(flags, DRAW_FLAG_LEFT))
+	{
+		cols--;
+	}
 
-	while (*txt && lines > rows)
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED) || HAS_BIT(flags, DRAW_FLAG_RIGHT))
 	{
-		txt = strchr(txt, '\n');
-		txt++;
-		lines--;
+		cols--;
 	}
 
-	arg = txt;
+	lines = 0;
+
 	row = top_row;
 	col = top_col;
 
-	while (*arg)
+	strcpy(arg1, "");
+	strcpy(arg2, "");
+
+	while (lines < rows)
 	{
-		arg = strchr(arg, '\n');
+		if (HAS_BIT(flags, DRAW_FLAG_LEFT))
+		{
+			strcpy(arg1, draw_vertical(flags, "│"));
+		}
 
-		*arg++ = 0;
+		if (HAS_BIT(flags, DRAW_FLAG_RIGHT))
+		{
+			strcpy(arg2, draw_vertical(flags, "│"));
+		}
 
 		goto_pos(ses, row++, col);
 
-		printf("%s", txt);
+		print_stdout("%s%s\e[%dC%s%s", color, arg1, cols, color, arg2);
 
-		txt = arg;
+		lines++;
 	}
 	pop_call();
 	return;
 }
-
-DO_DRAW(draw_vertical_line)
-{
-	int row = top_row;
-
-	arg = arg1;
-
-	arg1 += sprintf(arg1, "\e[%d;%dH%s", top_row, top_col, draw_vertical(flags, "│"));
-
-	for (row = top_row + 1 ; row <= bot_row ; row++)
-	{
-		arg1 += sprintf(arg1, "\e[%d;%dH%s", row, top_col, draw_vertical(flags, "│"));
-	}
-
-	printf("%s", arg);
-}

+ 35 - 8
src/event.c

@@ -30,6 +30,7 @@
 DO_COMMAND(do_event)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
+	struct listnode *node;
 	int cnt;
 
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
@@ -60,7 +61,11 @@ DO_COMMAND(do_event)
 			{
 				show_message(ses, LIST_EVENT, "#EVENT {%s} HAS BEEN SET TO {%s}.", arg1, arg2);
 
-				update_node_list(ses->list[LIST_EVENT], arg1, arg2, "", "");
+				SET_BIT(ses->event_flags, event_table[cnt].flags);
+
+				node = update_node_list(ses->list[LIST_EVENT], arg1, arg2, "", "");
+
+				node->data = event_table[cnt].flags;
 
 				return ses;
 			}
@@ -85,7 +90,7 @@ int check_all_events(struct session *ses, int flags, int args, int vars, char *f
 	va_list list;
 	int cnt;
 
-	if (gtd->ignore_level)
+	if (gtd->level->ignore)
 	{
 		return 0;
 	}
@@ -135,7 +140,12 @@ int check_all_events(struct session *ses, int flags, int args, int vars, char *f
 
 				if (HAS_BIT(ses_ptr->list[LIST_EVENT]->flags, LIST_FLAG_DEBUG))
 				{
-					show_debug(ses_ptr, LIST_ACTION, "#DEBUG EVENT {%s} (%s}", node->arg1, node->arg2);
+					show_debug(ses_ptr, LIST_EVENT, "#DEBUG EVENT {%s} (%s}", node->arg1, node->arg2);
+				}
+
+				if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+				{
+					delete_node_list(ses, LIST_EVENT, node);
 				}
 
 				script_driver(ses_ptr, LIST_EVENT, buf);
@@ -163,6 +173,9 @@ void mouse_handler(struct session *ses, int flags, int row, int col, char type)
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], line[BUFFER_SIZE], word[BUFFER_SIZE];
 	static char last[100];
 	static long long click[3];
+	int debug, info;
+
+	push_call("mouse_handler(%p,%d,%d,%d,%c)",ses,flags,row,col,type);
 
 	if (HAS_BIT(flags, MOUSE_FLAG_MOTION))
 	{
@@ -225,14 +238,16 @@ void mouse_handler(struct session *ses, int flags, int row, int col, char type)
 		strcat(arg2, "MOUSE BUTTON ");
 	}
 
-	if (row-1 < 0)
+	if (row - 1 < 0)
 	{
-		tintin_printf2(ses, "weird error (row,col) (%d,%d)", row, col);
+		tintin_printf2(ses, "mouse_handler: bad row: (row,col)=(%d,%d)", row, col);
+		pop_call();
 		return;
 	}
 	else if (row - 1 > gtd->screen->rows)
 	{
-		tintin_printf2(ses, "weird error (row,col) (%d,%d)", row, col);
+		tintin_printf2(ses, "mouse_handler: bad col: (row,col)=(%d,%d)", row, col);
+		pop_call();
 		return;
 	}
 	else
@@ -247,11 +262,11 @@ void mouse_handler(struct session *ses, int flags, int row, int col, char type)
 	{
 		if (HAS_BIT(flags, MOUSE_FLAG_BUTTON_A) && HAS_BIT(flags, MOUSE_FLAG_BUTTON_B))
 		{
-			strcat(arg2, "FOUR");
+			strcat(arg2, "RIGHT");
 		}
 		else if (HAS_BIT(flags, MOUSE_FLAG_BUTTON_B))
 		{
-			strcat(arg2, "THREE");
+			strcat(arg2, "LEFT");
 		}
 		else if (HAS_BIT(flags, MOUSE_FLAG_BUTTON_A))
 		{
@@ -282,6 +297,13 @@ void mouse_handler(struct session *ses, int flags, int row, int col, char type)
 		}
 	}
 
+
+	debug = HAS_BIT(ses->flags, SES_FLAG_MOUSEDEBUG) ? 1 : 0;
+	info  = HAS_BIT(ses->flags, SES_FLAG_MOUSEINFO) ? 1 : 0;
+
+	gtd->level->debug += debug;
+	gtd->level->info  += info;
+
 	check_all_buttons(ses, row, col, arg1, arg2, word, line);
 
 	check_all_events(ses, SUB_ARG, 2, 6, "%s %s", arg1, arg2, ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
@@ -368,5 +390,10 @@ void mouse_handler(struct session *ses, int flags, int row, int col, char type)
 			map_mouse_handler(ses, "SHORT-CLICKED", arg2, col, row);
 		}
 	}
+
+	gtd->level->debug -= debug;
+	gtd->level->info  -= info;
+
+	pop_call();
 	return;
 }

+ 46 - 20
src/files.c

@@ -41,14 +41,19 @@ DO_COMMAND(do_read)
 
 	if ((fp = fopen(filename, "r")) == NULL)
 	{
+		check_all_events(ses, SUB_ARG, 0, 2, "READ ERROR", filename, "FILE NOT FOUND.");
+
 		tintin_printf(ses, "#READ {%s} - FILE NOT FOUND.", filename);
+
 		return ses;
 	}
 
 	temp[0] = getc(fp);
 
-	if (!isgraph((int) temp[0]) || isalpha((int) temp[0]))
+	if (!ispunct((int) temp[0]))
 	{
+		check_all_events(ses, SUB_ARG, 0, 2, "READ ERROR", filename, "INVALID START OF FILE");
+
 		tintin_printf(ses, "#ERROR: #READ {%s} - INVALID START OF FILE.", filename);
 
 		fclose(fp);
@@ -70,6 +75,8 @@ DO_COMMAND(do_read)
 
 	if ((bufi = (char *) calloc(1, filedata.st_size + 2)) == NULL || (bufo = (char *) calloc(1, filedata.st_size + 2)) == NULL)
 	{
+		check_all_events(ses, SUB_ARG, 0, 2, "READ ERROR", filename, "FAILED TO ALLOCATE MEMORY");
+
 		tintin_printf(ses, "#ERROR: #READ {%s} - FAILED TO ALLOCATE MEMORY.", filename);
 
 		fclose(fp);
@@ -88,7 +95,7 @@ DO_COMMAND(do_read)
 	{
 		if (com == 0)
 		{
-			if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+			if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 			{
 				*pto++ = *pti++;
 				*pto++ = *pti++;
@@ -263,6 +270,8 @@ DO_COMMAND(do_read)
 
 	if (lvl)
 	{
+		check_all_events(ses, SUB_ARG, 0, 2, "READ ERROR", filename, "MISSING BRACE OPEN OR CLOSE");
+
 		tintin_printf(ses, "#ERROR: #READ {%s} - MISSING %d '%c' BETWEEN LINE %d AND %d.", filename, abs(lvl), lvl < 0 ? DEFAULT_OPEN : DEFAULT_CLOSE, fix == 0 ? 1 : ok, fix == 0 ? lnc + 1 : fix);
 
 		fclose(fp);
@@ -275,6 +284,8 @@ DO_COMMAND(do_read)
 
 	if (com)
 	{
+		check_all_events(ses, SUB_ARG, 0, 2, "READ ERROR", filename, "MISSING COMMENT OPEN OR CLOSE");
+
 		tintin_printf(ses, "#ERROR: #READ {%s} - MISSING %d '%s'", filename, abs(com), com < 0 ? "/*" : "*/");
 
 		fclose(fp);
@@ -287,11 +298,23 @@ DO_COMMAND(do_read)
 
 	sprintf(temp, "{TINTIN CHAR} {%c}", bufo[0]);
 
-	gtd->quiet_level++;
+	if (bufo[0] != '#')
+	{
+		gtd->level->verbose++;
+		gtd->level->debug++;
+
+		show_error(ses, LIST_COMMAND, "\e[1;5;31mWARNING: SETTING THE COMMAND CHARACTER TO '%c' BECAUSE IT'S THE FIRST CHARACTER IN THE FILE.", bufo[0]);
+
+		gtd->level->debug--;
+		gtd->level->verbose--;
+	}
+
+	gtd->level->quiet++;
 
 	do_configure(ses, temp);
 
 	lvl = 0;
+	lnc = 0;
 	pti = bufo;
 	pto = bufi;
 
@@ -302,24 +325,13 @@ DO_COMMAND(do_read)
 			*pto++ = *pti++;
 			continue;
 		}
+		lnc++;
 		*pto = 0;
 
 		if (strlen(bufi) >= BUFFER_SIZE)
 		{
-/*			gtd->quiet_level--;
-
-			bufi[20] = 0;
-*/
-//			tintin_printf(ses, "#ERROR: #READ {%s} - BUFFER OVERFLOW AT COMMAND: %.20s.", filename, bufi);
-			tintin_printf(ses, "#ERROR: #READ {%s} - BUFFER OVERFLOW AT COMMAND:", filename);
-/*
-			fclose(fp);
-
-			free(bufi);
-			free(bufo);
-
-			return ses;
-*/		}
+			tintin_printf(ses, "#ERROR: #READ {%s} - BUFFER OVERFLOW AT COMMAND: %.30s", filename, bufi);
+		}
 
 		if (bufi[0])
 		{
@@ -329,7 +341,7 @@ DO_COMMAND(do_read)
 		pti++;
 	}
 
-	gtd->quiet_level--;
+	gtd->level->quiet--;
 
 	if (!HAS_BIT(ses->flags, SES_FLAG_VERBOSE))
 	{
@@ -367,6 +379,8 @@ DO_COMMAND(do_write)
 	FILE *file;
 	char filename[BUFFER_SIZE], forceful[BUFFER_SIZE];
 	struct listroot *root;
+	struct listnode *node;
+
 	int i, j, fix, cnt = 0;
 
 	arg = get_arg_in_braces(ses, arg, filename, GET_ONE);
@@ -374,6 +388,8 @@ DO_COMMAND(do_write)
 
 	if (*filename == 0)
 	{
+		check_all_events(ses, SUB_ARG, 0, 2, "WRITE ERROR", filename, "INVALID FILE NAME");
+
 		tintin_printf2(ses, "#SYNTAX: #WRITE {<filename>} {[FORCE]}");
 
 		return ses;
@@ -381,6 +397,7 @@ DO_COMMAND(do_write)
 	
 	if (!str_suffix(filename, ".map") && !is_abbrev(forceful, "FORCE"))
 	{
+		check_all_events(ses, SUB_ARG, 0, 2, "WRITE ERROR", filename, "INVALID FILE EXTENSION");
 		tintin_printf2(ses, "#WRITE {%s}: USE {FORCE} TO OVERWRITE .map FILES.", filename);
 
 		return ses;
@@ -388,6 +405,8 @@ DO_COMMAND(do_write)
 
 	if ((file = fopen(filename, "w")) == NULL)
 	{
+		check_all_events(ses, SUB_ARG, 0, 2, "WRITE ERROR", filename, "FAILED TO OPEN");
+
 		tintin_printf(ses, "#ERROR: #WRITE: COULDN'T OPEN {%s} TO WRITE.", filename);
 
 		return ses;
@@ -406,9 +425,16 @@ DO_COMMAND(do_write)
 
 		for (j = 0 ; j < root->used ; j++)
 		{
-			if (*root->list[j]->group == 0)
+			node = root->list[j];
+
+			if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+			{
+				continue;
+			}
+
+			if (*node->group == 0)
 			{
-				write_node(ses, i, root->list[j], file);
+				write_node(ses, i, node, file);
 
 				cnt++;
 				fix++;

+ 420 - 237
src/help.c

@@ -90,7 +90,7 @@ DO_COMMAND(do_help)
 
 		for (cnt = 0 ; *help_table[cnt].name != 0 ; cnt++)
 		{
-			if (strlen(buf) + 19 > gtd->screen->cols)
+			if (strlen(buf) + 19 > ses->wrap)
 			{
 				print_lines(ses, SUB_COL, "<088>%s<088>\n", buf);
 
@@ -119,8 +119,6 @@ DO_COMMAND(do_help)
 
 		for (cnt = 0 ; *help_table[cnt].name != 0 ; cnt++)
 		{
-//			printf("debug: %s\n", help_table[cnt].name);
-
 			if (cnt && cnt % 4 == 0)
 			{
 				substitute(ses, buf, buf, SUB_ESC|SUB_COL);
@@ -355,9 +353,9 @@ struct help_type help_table[] =
 	},
 	{
 		"BELL",
-		"<178>Command<278>: #bell\n"
+		"<178>Command<278>: #bell <178>{<278>flash<178>|<278>focus<178>|<278>margin<178>|<278>ring<178>|<278>volume<178>} {<278>argument<178>}<278>\n"
 		"\n"
-		"         The #bell command will ring the terminal bell.\n"
+		"         The #bell command without an argument will ring the terminal bell.\n"
 		"\n"
 		"<178>Example<278>: #action {Bubba tells you} {#bell}\n"
 		"\n"
@@ -366,23 +364,24 @@ struct help_type help_table[] =
 		"         use #system to play a sound file.\n"
 		"\n"
 		"         Some terminals will allow you to use VT100 Operating System Commands\n"
-		"         to change the terminal's title which can be used as a visual alert.\n"
+		"         to change the terminal's bell behavior which can be used to flash the\n"
+		"         taskbar icon and or focus the window on receival of a bell.\n"
 		"\n"
 		"<178>Example<278>: #action {Bubba tells you} {#screen save title;#screen set title Tell!;\n"
-		"           #bell;#delay 10 #screen load title}\n"
+		"           #bell ring;#delay 10 #screen load title}\n"
 		"\n"
 		"         The above example will save your window title, change the title to\n"
-		"         'Tell!', next reset the window title after 10 seconds.\n"
+		"         'Tell!', ring the bell, next reset the window title after 10 seconds.\n"
 		"\n"
 		"         It's possible to set the terminal to pop to the foreground upon\n"
 		"         ringing of the alarm bell.\n"
 		"\n"
-		"<178>Example<278>: #showme {pop up alarm: \\e[?1043h\\a\\e[?1043l}\n"
+		"<178>Example<278>: #bell focus on;#bell ring;#bell focus off\n"
 		"\n"
 		"         It's possible to adjust the alarm bell volume on some terminals.\n"
 		"\n"
 		"<178>Example<278>: #loop {1} {8} {cnt} {#line substitute variables\n"
-		"           #delay {$cnt} #showme {Volume $cnt: \\e[$cnt t};#bell}\n",
+		"           #delay {$cnt} #showme {Volume $cnt: #bell volume $cnt;#bell}\n",
 
 		"screen"
 	},
@@ -670,12 +669,25 @@ struct help_type help_table[] =
 		"         For 12 bit truecolor use <<888>F000> to <<888>FFFF> for foreground colors and\n"
 		"         <<888>B000> to <<888>BFFF> for background colors.\n"
 		"\n"
-		"         For 24 bit truecolor use \\e[38;2;R;G;Bm where R G B are red/green/blue\n"
-		"         intensities between 0 and 255. For example: \\e[37;2;50;100;150m. Use\n"
-		"         \\e[48;2;R;G;Bm for background colors.\n",
-		
+		"         For 24 bit truecolor use <<888>F000000> to <<888>FFFFFFF> for foreground\n"
+		"         colors and <<888>B000000> to <<888>BFFFFFF> for background colors.\n"
+		"\n"
+		"         If the color code exceeds your configured color mode it will be\n"
+		"         downgraded to the closest match.\n",
+
 		"characters coordinates escape mathematics pcre"
 	},
+	{
+		"COMMANDS",
+		
+		"<178>Command<278>: #commands <178>{<278>regex<178>}\n"
+		"<278>\n"
+		"         Shows all commands or all commands matching the given search\n"
+		"         string.\n",
+		
+		"help info statements"
+	},
+
 	{
 		"COORDINATES",
 
@@ -707,13 +719,15 @@ struct help_type help_table[] =
 		"\n"
 		"         Config options which aren't listed by default:\n"
 		"\n"
+		"         #CONFIG {BUFFER SIZE}    {SIZE} Set the scrollback buffer size.\n"
 		"         #CONFIG {CHILD LOCK}   {ON|OFF} Enable or disable command input.\n"
 		"         #CONFIG {CONVERT META} {ON|OFF} Shows color codes and key bindings.\n"
 		"         #CONFIG {DEBUG TELNET} {ON|OFF} Shows telnet negotiations y/n.\n"
 		"         #CONFIG {LOG LEVEL}  {LOW|HIGH} LOW logs mud output before triggers.\n"
 		"         #CONFIG {INHERITANCE}  {ON|OFF} Session trigger inheritance y/n.\n"
-		"         #CONFIG {MCCP}         {ON|OFF} Enable or disable MCCP support.\n",
-		
+		"         #CONFIG {MCCP}         {ON|OFF} Enable or disable MCCP support.\n"
+		"         #CONFIG {PID}          {NUMBER} Set the PID of the master process.\n",
+
 		"class line"
 	},
 	{
@@ -757,6 +771,31 @@ struct help_type help_table[] =
 		
 		"alias history keypad macro speedwalk tab"
 	},
+	{
+		"DAEMON",
+		
+		"<178>Command<278>: #daemon <178>{<278>attach<178>|<278>detach<178>|<278>kill<178>|<278>list<178>} <178>[<278>name<178>]\n"
+		"\n"
+		"         <278>#daemon provides functionality similar to that of the screen and tmux\n"
+		"         utilities.\n"
+		"\n"
+		"         <178>#daemon attach [name]\n"
+		"         <278>  The attach option will try to find a daemonized tintin instance and\n"
+		"           take over control. The name argument is optional.\n"
+		"\n"
+		"         <178>#daemon detach [name]\n"
+		"         <278>  The detach option will daemonize tintin, turning it into a background\n"
+		"           process. The name argument is optional and is useful if you have\n"
+		"           several daemonized tt++ instances running so you can keep them apart.\n"
+		"\n"
+		"         <178>#daemon kill [name]\n"
+		"         <278>  Kills all daemons or daemons with matching name.\n"
+		"\n"
+		"         <178>#daemon list [name]\n"
+		"         <278>  List all daemons or daemons with matching name.\n",
+		
+		"script system run"
+	},
 	{
 		"DEBUG",
 
@@ -790,7 +829,7 @@ struct help_type help_table[] =
 		"DELAY",
 
 		"<178>Command<278>: #delay <178>{<278>seconds<178>} {<278>command<178>}<278>\n"
-		"<178>Command<278>: #delay <178>{<278>name<178>} {<278>command<178>} {<278>seconds<178>}<278> \n"
+		"<178>Command<278>: #delay <178>{<278>name<178>} {<278>command<178>} {<278>seconds<178>}<278>\n"
 		"\n"
 		"         Delay allows you to have tintin wait the given amount of seconds\n"
 		"         before executing the given command. tintin won't wait before\n"
@@ -808,29 +847,56 @@ struct help_type help_table[] =
 		
 		"event ticker"
 	},
+
 	{
 		"DRAW",
-		
-		"<178>Command<278>: #draw <178>{<278>option<178>} {<278>square<178>} {<278>argument<178>}\n"
+
+		"<178>Command<278>: #draw <178>[<278>color<178>] <178>[<278>options<178>] <178><<278>type<178>> <<278>square<178>> {<278>text<178>}\n"
 		"<278>\n"
-		"         The draw commands allows you to draw various lines and shapes on the\n"
-		"         screen. Available options and a brief description are provided when\n"
-		"         you type #draw without an argument.\n"
+		"         The draw commands allows you to draw various types of lines and shapes\n"
+		"         on the screen. Common options and types with a brief description are\n"
+		"         provided when you type #draw without an argument.\n"
 		"\n"
-		"         The square argument should exists of two coordinates defining the\n"
+		"         The square arguments should exists of two coordinates defining the\n"
 		"         upper left and bottom right corner using row, col, row, col syntax.\n"
 		"\n"
-		"         You can prefix the option as following:\n"
-		"\n"
-		"         PRUNED  will prune the corners of any drawn shape.\n"
-		"         ROUNDED will round the corners.\n"
-		"         CROSSED will cross the corners.\n"
-		"\n"
-		"         Some draw options take an additional argument, most notably the\n"
-		"         BOX TEXT option which allows you to place the given text inside\n"
-		"         the drawn box.\n"
-		"\n"
-		"<178>Example<278>: #draw {box text} 1 1 3 20 {Hello world!}\n",
+		"\n       You can prefix the option with a color code or color name to color the\n"
+		"         lines and shapes.\n"
+		"\n"
+		"         You can further prefix the option as following:\n"
+		"\n"
+		"         ASCII      will draw in ASCII mode.\n"
+		"         BLANKED    will blank the lines and corners.\n"
+		"         BOTTOM     will draw on the bottom side if possible.\n"
+		"         BUMPED     will precede the draw with an enter.\n"
+		"         CIRCLED    will circle the corners.\n"
+		"         CONVERT    will draw text with meta conversion.\n"
+		"         CORNERED   will draw lines with corners if possible.\n"
+		"         CROSSED    will cross the corners.\n"
+		"         HORIZONTAL will draw horizontal if possible.\n"
+		"         JEWELED    will diamond the corners.\n"
+		"         LEFT       will draw on the left side if possible.\n"
+		"         PRUNED     will prune the corners.\n"
+		"         RIGHT      will draw on the right side if possible.\n"
+		"         ROUNDED    will round the corners.\n"
+		"         TEED       will tee the corners.\n"
+		"         TOP        will draw on the top side if possible.\n"
+		"         TUBED      will draw tubes instead of lines.\n"
+		"         UNICODE    will draw in unicode mode.\n"
+		"         VERTICAL   will draw vertical if possible.\n"
+		"\n"
+		"         The following types are available.\n"
+		"\n"
+		"         BOX        will draw a box.\n"
+		"         LINE       will draw a line.\n"
+		"         SIDE       will draw one or more sides of a box.\n"
+		"         TILE       will draw a tile\n"
+		"\n"
+		"         All draw types take an optional text argument as long as a valid\n"
+		"         square with enough space has been defined. Text is automatically\n"
+		"         word wrapped.\n"
+		"\n"
+		"<178>Example<278>: #draw {box} 1 1 3 20 {Hello world!}\n",
 
 		"buffer echo grep showme"
 	},
@@ -929,74 +995,79 @@ struct help_type help_table[] =
 		"         Some events can be prefixed with CATCH to interrupt default behavior.\n"
 		"\n"
 		"         CATCH <EVENT>\n"
-		"         CHAT MESSAGE          %0 default %1 plain\n"
-		"         CLASS ACTIVATED       %0 class name\n"
-		"         CLASS DEACTIVATED     %0 class name\n"
-		"         DATE                  %1 month - %3 day   %4 hour : %5 minute\n"
-		"         DAY <DAY>             %3 day of the month\n"
-		"         DOUBLE-CLICKED <VAR>  %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
+		"         CHAT MESSAGE           %0 default %1 plain\n"
+		"         CLASS ACTIVATED        %0 class name\n"
+		"         CLASS DEACTIVATED      %0 class name\n"
+		"         DATE                   %1 month - %3 day   %4 hour : %5 minute\n"
+		"         DAY <DAY>              %3 day of the month\n"
+		"         DOUBLE-CLICKED <VAR>   %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
 		"         END OF PATH\n"
-		"         HOUR                  %4 hour\n"
+		"         HOUR                   %4 hour\n"
 		"         IAC <VAR> <VAR>\n"
-		"         IAC SB GMCP <MODULE>  %0 data     %1 raw data\n"
-		"         IAC SB MSSP           %0 variable %1 value\n"
-		"         IAC SB MSDP           %0 variable %1 value\n"
-		"         IAC SB MSDP <VAR>     %1 value\n"
-		"         IAC SB NEW-ENVIRON    %0 variable %1 value\n"
-		"         IAC SB ZMP <VAR>      %0 value\n"
-		"         IAC SB <VAR>          %0 raw text %1 raw data\n"
-		"         LONG-CLICKED <VAR>    %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
-		"         MAP ENTER MAP         %0 new vnum\n"
-		"         MAP ENTER ROOM        %0 new vnum %1 old vnum\n"
-		"         MAP ENTER ROOM <VAR>  %0 new vnum %1 old vnum\n"
-		"         MAP EXIT MAP          %0 old vnum\n"
-		"         MAP EXIT ROOM         %0 old vnum %1 new vnum\n"
-		"         MAP EXIT ROOM <VAR>   %0 old vnum %1 new vnum\n"
-		"         MAP FOLLOW MAP        %0 old vnum %1 new vnum %2 exit name\n"
+		"         IAC SB GMCP <MODULE>   %0 data     %1 raw data\n"
+		"         IAC SB MSSP            %0 variable %1 value\n"
+		"         IAC SB MSDP            %0 variable %1 value\n"
+		"         IAC SB MSDP <VAR>      %1 value\n"
+		"         IAC SB NEW-ENVIRON     %0 variable %1 value\n"
+		"         IAC SB ZMP <VAR>       %0 value\n"
+		"         IAC SB <VAR>           %0 raw text %1 raw data\n"
+		"         LONG-CLICKED <VAR>     %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
+		"         MAP ENTER MAP          %0 new vnum\n"
+		"         MAP ENTER ROOM         %0 new vnum %1 old vnum\n"
+		"         MAP ENTER ROOM <VAR>   %0 new vnum %1 old vnum\n"
+		"         MAP EXIT MAP           %0 old vnum\n"
+		"         MAP EXIT ROOM          %0 old vnum %1 new vnum\n"
+		"         MAP EXIT ROOM <VAR>    %0 old vnum %1 new vnum\n"
+		"         MAP FOLLOW MAP         %0 old vnum %1 new vnum %2 exit name\n"
 		"         MAP UPDATED VTMAP\n"
-		"         MINUTE                %5 minute\n"
-		"         MONTH                 %1 month\n"
-		"         MOVED <VAR>           %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
-		"         PORT CONNECTION       %0 name %1 ip %2 port\n"
-		"         PORT DISCONNECTION    %0 name %1 ip %2 port\n"
-		"         PORT MESSAGE          %0 data %1 plain data\n"
-		"         PORT LOG MESSAGE      %0 name %1 ip %2 port %3 data %4 plain data\n"
-		"         PORT RECEIVED MESSAGE %0 name %1 ip %2 port %3 data %4 plain data\n"
-		"         PRESSED <VAR>         %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
-		"         PROGRAM START         %0 startup arguments\n"
-		"         PROGRAM TERMINATION   %0 goodbye message\n"
-		"         RECEIVED INPUT        %0 raw text\n"
-		"         RECEIVED KEYPRESS     %0 raw text %1 unicode index\n"
-		"         RECEIVED LINE         %0 raw text %1 plain text\n"
-		"         RECEIVED OUTPUT       %0 raw text\n"
-		"         RECEIVED PROMPT       %0 raw text %1 plain text\n"
-		"         RELEASED <VAR>        %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
-		"         SCAN CSV HEADER       %0 all args %1 arg1 %2 arg2 .. %99 arg99\n"
-		"         SCAN CSV LINE         %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
-		"         SCAN TSV HEADER       %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
-		"         SCAN TSV LINE         %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
-		"         SCREEN RESIZE         %0 rows %1 cols %1 height %2 width\n"
-		"         SCROLLED <VAR>        %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
-		"         SECOND                %6 second\n"
-		"         SEND OUTPUT           %0 raw text %1 size\n"
-		"         SENT OUTPUT           %0 raw text %1 size\n"
-		"         SESSION ACTIVATED     %0 name\n"
-		"         SESSION CONNECTED     %0 name %1 host %2 ip %3 port\n"
-		"         SESSION CREATED       %0 name %1 host %2 ip %3 port\n"
-		"         SESSION DEACTIVATED   %0 name\n"
-		"         SESSION DISCONNECTED  %0 name %1 host %2 ip %3 port\n"
-		"         SESSION TIMED OUT     %0 name %1 host %2 ip %3 port\n"
-		"         SHORT-CLICKED <VAR>   %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
-		"         SYSTEM ERROR          %0 name %1 system msg %2 error %3 error msg\n"
-		"         TIME                  %4 hour : %5 minute : %6 second\n"
-		"         TRIPLE-CLICKED <VAR>  %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
-		"         UNKNOWN COMMAND       %0 raw text\n"
-		"         VARIABLE UPDATE <VAR> %0 name %1 value\n"
-		"         VT100 SCROLL REGION   %0 top row %1 bot row %2 rows %3 cols %4 wrap\n"
-		"         WEEK <DAY>            %2 day of the week\n"
-		"         WINDOW FOCUS IN       %0 name\n"
-		"         WINDOW FOCUS OUT      %0 name\n"
-		"         YEAR                  %0 year\n"
+		"         MINUTE                 %5 minute\n"
+		"         MONTH                  %1 month\n"
+		"         MOVED <VAR>            %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
+		"         PORT CONNECTION        %0 name %1 ip %2 port\n"
+		"         PORT DISCONNECTION     %0 name %1 ip %2 port\n"
+		"         PORT MESSAGE           %0 data %1 plain data\n"
+		"         PORT LOG MESSAGE       %0 name %1 ip %2 port %3 data %4 plain data\n"
+		"         PORT RECEIVED MESSAGE  %0 name %1 ip %2 port %3 data %4 plain data\n"
+		"         PRESSED <VAR>          %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
+		"         PROGRAM START          %0 startup arguments\n"
+		"         PROGRAM TERMINATION    %0 goodbye message\n"
+		"         READ ERROR             %0 filename %1 error message\n"
+		"         RECEIVED INPUT         %0 raw text\n"
+		"         RECEIVED KEYPRESS      %0 raw text %1 unicode index\n"
+		"         RECEIVED LINE          %0 raw text %1 plain text\n"
+		"         RECEIVED OUTPUT        %0 raw text\n"
+		"         RECEIVED PROMPT        %0 raw text %1 plain text\n"
+		"         RELEASED <VAR>         %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
+		"         SCAN CSV HEADER        %0 all args %1 arg1 %2 arg2 .. %99 arg99\n"
+		"         SCAN CSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
+		"         SCAN TSV HEADER        %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
+		"         SCAN TSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
+		"         SCREEN RESIZE          %0 rows %1 cols %1 height %2 width\n"
+		"         SCREEN SPLIT           %0 top row %1 top col %2 bot row %3 bot col\n"
+		"         SCREEN UNSPLIT         %0 top row %1 top col %2 bot row %3 bot col\n"
+		"         SCROLLED <VAR>         %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
+		"         SECOND                 %6 second\n"
+		"         SEND OUTPUT            %0 raw text %1 size\n"
+		"         SENT OUTPUT            %0 raw text %1 size\n"
+		"         SESSION ACTIVATED      %0 name\n"
+		"         SESSION CONNECTED      %0 name %1 host %2 ip %3 port\n"
+		"         SESSION CREATED        %0 name %1 host %2 ip %3 port\n"
+		"         SESSION DEACTIVATED    %0 name\n"
+		"         SESSION DISCONNECTED   %0 name %1 host %2 ip %3 port\n"
+		"         SESSION TIMED OUT      %0 name %1 host %2 ip %3 port\n"
+		"         SHORT-CLICKED <VAR>    %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
+		"         SYSTEM ERROR           %0 name %1 system msg %2 error %3 error msg\n"
+		"         TIME                   %4 hour : %5 minute : %6 second\n"
+		"         TRIPLE-CLICKED <VAR>   %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
+		"         UNKNOWN COMMAND        %0 raw text\n"
+		"         VARIABLE UPDATE <VAR>  %0 name %1 new value\n"
+		"         VARIABLE UPDATED <VAR> %0 name %1 new value\n"
+		"         VT100 SCROLL REGION    %0 top row %1 bot row %2 rows %3 cols %4 wrap\n"
+		"         WEEK <DAY>             %2 day of the week\n"
+		"         WINDOW FOCUS IN        %0 name\n"
+		"         WINDOW FOCUS OUT       %0 name\n"
+		"         WRITE ERROR            %0 filename %1 error message\n"
+		"         YEAR                   %0 year\n"
 		"\n"
 		"         To see all events trigger use #event info on. Since this can quite\n"
 		"         spammy it's possible to gag event info messages.\n"
@@ -1062,12 +1133,9 @@ struct help_type help_table[] =
 		"                                         optional {{string}{width}} syntax\n"
 		"         #format {test} {%x}      {hex}  print corresponding charset character\n"
 		"         #format {test} {%A}     {char}  store corresponding character value\n"
-		"         #format {cols} {%C}         {}  store the screen width in {cols}\n"
 		"         #format {test} {%D}      {hex}  convert hex to decimal in {test}\n"
 		"         #format {hash} {%H}   {string}  store a 64 bit string hash in {hash}\n"
 		"         #format {test} {%L}   {string}  store the string length in {test}\n"
-		"         #format {rows} {%R}         {}  store the screen height in {rows}\n"
-		"         #format {name} {%S}         {}  store the session name in {name}\n"
 		"         #format {time} {%T}         {}  store the epoch time in {time}\n"
 		"         #format {time} {%U}         {}  store the micro epoch time in {time}\n"
 		"         #format {test} {%X}      {dec}  convert dec to hexadecimal in {test}\n\n"
@@ -1161,27 +1229,45 @@ struct help_type help_table[] =
 		"\n"
 		"         Using #help %* will display all help entries.\n",
 		
-		"debug ignore info message"
+		"commands debug ignore info message statements"
 	},
 	{
 		"HIGHLIGHT",
 
 		"<178>Command<278>: #highlight <178>{<278>string<178>} {<278>color names<178>}<278>\n"
 		"\n"
-		"         The highlight command is used to allow you to highlight strings of text\n"
-		"         from the mud.  Available ANSI color names are:\n"
-		"\n"
-		"         reset, light, dark, underscore, blink, reverse\n"
-		"\n"
-		"         black, red, green, yellow, blue, magenta, cyan, white,\n"
-		"         b black, b red, b green, b yellow, b blue, b magenta, b cyan, b white\n"
-		"\n"
-		"         Available XTERM 256 color names are:\n"
-		"\n"
-		"         azure, ebony, jade, lime, orange, pink, silver, tan, violet,\n"
-		"         light azure, light ebony, light jade, light lime, light orange,\n"
-		"         light pink, light silver, light tan, light violet.\n"
+		"         The highlight command is used to allow you to highlight strings of text.\n"
+		"\n"
+		"         Available color options are:\n"
+		"\n"
+		"         reset      - resets the color state to default\n"
+		"         light      - turns the color light in 16 color mode.\n"
+		"         dark       - turns the color dark in 16 color mode.\n"
+		"         underscore - underscores the text.\n"
+		"         blink      - makes the text blink.\n"
+		"         reverse    - reverse foreground and background color.\n"
+		"         b          - makes next color the background color.\n"
+		"\n"
+		"         Available color names are:\n"
+		"\n"
+		"         <<888>F06B> - azure                 <<888>F08F> - Azure\n"
+		"         <<888>F00B> - blue                  <<888>F00F> - Blue\n"
+		"         <<888>F0BB> - cyan                  <<888>F0FF> - Cyan\n"
+		"         <<888>F000> - ebony                 <<888>F666> - Ebony\n"
+		"         <<888>F0B0> - green                 <<888>F0F0> - Green\n"
+		"         <<888>F0B6> - jade                  <<888>F0F8> - Jade\n"
+		"         <<888>F6B0> - lime                  <<888>F8F0> - Lime\n"
+		"         <<888>FB0B> - magenta               <<888>FF0F> - Magenta\n"
+		"         <<888>FB60> - orange                <<888>FF80> - Orange\n"
+		"         <<888>FB06> - pink                  <<888>FF08> - Pink\n"
+		"         <<888>FB00> - red                   <<888>FF00> - Red\n"
+		"         <<888>F888> - silver                <<888>FDDD> - Silver\n"
+		"         <<888>F860> - tan                   <<888>FDB0> - Tan\n"
+		"         <<888>F60B> - violet                <<888>F80F> - Violet\n"
+		"         <<888>FBBB> - white                 <<888>FFFF> - White\n"
+		"         <<888>FBB0> - yellow                <<888>FFF0> - Yellow\n"
 		"\n"
+
 		"         The %1-99 variables can be used as 'wildcards' that will match with any\n"
 		"         text. They are useful for highlighting a complete line. The %0 variable\n"
 		"         should never be used in highlights.\n"
@@ -1191,14 +1277,14 @@ struct help_type help_table[] =
 		"\n"
 		"         Besides color names also <<888>abc> color codes can be used.\n"
 		"\n"
-		"<178>Example<278>: #high {Valgar} {reverse}\n"
-		"         Prints every occurrence of 'Valgar' in reverse video.\n"
+		"<178>Example<278>: #high {Valgar} {reverse blink}\n"
+		"         Prints every occurrence of 'Valgar' in blinking reverse video.\n"
 		"\n"
 		"<178>Example<278>: #high {^You %1} {bold cyan}\n"
 		"         Boldfaces any line that starts with 'You' in cyan.\n"
 		"\n"
-		"<178>Example<278>: #high {Bubba} {red underscore blink}\n"
-		"         Highlights the name Bubba as blinking, red, underscored text\n"
+		"<178>Example<278>: #high {Bubba} {red underscore b green}\n"
+		"         Highlights the name Bubba as red underscored text on green background.\n"
 		"\n"
 		"<178>Comment<278>: See '#help action', for more information about triggers.\n"
 		"\n"
@@ -1527,7 +1613,7 @@ struct help_type help_table[] =
 		"         #map goto 1\n"
 		"\n"
 		"         These commands will create a 12 row vt100 split section at the top of\n"
-		"         your screen where a map drawn using unicode characters is displayed. \n"
+		"         your screen where a map drawn using unicode characters is displayed.\n"
 		"\n"
 		"<178>Example<278>: #action <178>{<278>There is no exit in that direction.<178>} {<278>#map undo<178>}<278>\n"
 		"\n"
@@ -1630,36 +1716,50 @@ struct help_type help_table[] =
 
 		"<178>Command<278>: #line <178>{<278>option<178>} {<278>argument<178>}<278>\n"
 		"\n"
-		"         #line background                       Prevent new session activation.\n"
+		"         <178>#line background <argument>\n"
+		"         <278>  Prevent new session activation.\n"
 		"\n"
-		"         #line gag                              Gag the next line.\n"
+		"         <178>#line debug <argument>\n"
+		"         <278>  Argument is executed in debug mode.\n"
 		"\n"
-		"         #line ignore {argument}                Argument is executed without\n"
-		"                                                any triggers being checked.\n"
+		"         <178>#line gag\n"
+		"         <278>                Gag the next line.\n"
 		"\n"
-		"         #line log {filename} {[text]}          Log the current or given line to\n"
-		"                                                file.\n"
+		"         <178>#line ignore {argument}\n"
+		"         <278>  Argument is executed without any triggers being checked.\n"
 		"\n"
-		"         #line logverbatim {filename} {[text]}  Log text without variable\n"
-		"                                                substitution.\n"
-
+		"         <178>#line log <filename> [text]\n"
+		"         <278>  Log the next line to file unless the [text] argument is\n"
+		"         <278>  provided.\n"
 		"\n"
-		"         #line quiet {argument}                 Argument is executed with\n"
-		"                                                suppression of system messages.\n"
+		"         <178>#line logmode <option> <argument>\n"
+		"         <278>  Argument is executed using the provided logmode, available\n"
+		"         <278>  modes are: html, plain, and raw.\n"
+//		"\n"
+//		"         <178>#line logverbatim {filename} {[text]}  Log text without variable\n"
+//		"         <278>                                  substitution.\n"
 		"\n"
-		"         #line strip {argument}                 Strips the argument of color\n"
-		"                                                codes next executes it as a\n"
-		"                                                command.\n"
+		"         <178>#line oneshot <argument>\n"
+		"         <278>  Argument is executed in oneshot mode, all triggers created will\n"
+		"         <278>  only fire once.\n"
 		"\n"
-		"         #line substitute {options} {argument}  Substitutes the given options:\n"
-		"                                                variables, functions, colors,\n"
-		"                                                escapes, secure, in the given\n"
-		"                                                argument next executes it as a\n"
-		"                                                command.\n"
+		"         <178>#line quiet <argument>\n"
+		"         <278>  Argument is executed with suppression of most system messages.\n"
 		"\n"
-		"         #line verbatim {argument}              Argument is executed verbatim.\n"
+		"         <178>#line strip <argument>\n"
+		"         <278>  Argument is executed with all color codes stripped.\n"
 		"\n"
-		"         #line verbose {argument}               Argument is executed verbose.\n"
+		"         <178>#line substitute <options> <argument>\n"
+		"         <278>  Argument is executed using the provided substitutions, available\n"
+		"         <278>  options are: arguments, colors, escapes, functions, secure, and\n"
+		"         <278>  variables.\n"
+		"\n"
+		"         <178>#line verbatim <argument>\n"
+		"         <278>  Argument is executed verbatim, prohibiting variable and function\n"
+		"         <278>  substitutions.\n"
+		"\n"
+		"         <178>#line verbose <argument>\n"
+		"         <278>  Argument is executed with most system messages enabled.\n"
 		"\n"
 		"         When using #line log and logging in html format use \\c< \\c> \\c& \\c\" to\n"
 		"         log a literal < > & and \".\n",
@@ -1798,6 +1898,9 @@ struct help_type help_table[] =
 		"         <178>#map at <exit|vnum> <command>\n"
 		"         <278>  Execute the command at the given exit or vnum.\n"
 		"\n"
+		"         <178>#map center <x> <y> <z>\n"
+		"         <278>  Sets displaying center of the map viewer, default is 0 0 0.\n"
+		"\n"
 		"         <178>#map color <field> [value]\n"
 		"         <278>  Sets the map color for the given color field.\n"
 		"\n"
@@ -1841,7 +1944,7 @@ struct help_type help_table[] =
 		"         <278>  must be matched, if <roomdesc>, <roomarea> or <roomnote> or\n"
 		"         <278>  <roomterrain> or <roomflag> is provided these are matched as\n"
 		"         <278>  well against the room to be found.\n"
-		"         <278>  These options are also available to the at, delete, goto\n\n"
+		"         <278>  These options are also available to the at, delete, goto\n"
 		"         <278>  link, list and run commands.\n"
 		"\n"
 		"         <178>#map flag asciigraphics\n"
@@ -1870,14 +1973,6 @@ struct help_type help_table[] =
 		"         <278>  screen if you have one. You can create a 16 rows high top\n"
 		"         <278>  screen by using '#split 16 1'.\n"
 		"\n"
-		"         <178>#map goto <room vnum> [dig]\n"
-		"         <278>  Takes you to the given room vnum, with the\n"
-		"         <278>  dig argument a new room will be created if non exists.\n"
-		"\n"
-		"         <178>#map goto <name> <exits> <desc> <area> <note> <terrain>\n"
-		"         <278>  Takes you to\n"
-		"         <278>  the given room name, if you provide exits those must match.\n"
-		"\n"
 		"         <178>#map get <option> <variable> [vnum]\n"
 		"         <278>  Store a map value into a variable, if no vnum is given the\n"
 		"         <278>  current room is used. Use 'all' as the option to store all\n"
@@ -1892,12 +1987,20 @@ struct help_type help_table[] =
 		"         <278>  recall location. The room can contain multiple exits, in case\n"
 		"         <278>  there are multiple commands that are similar to recall.\n"
 		"\n"
+		"         <178>#map goto <room vnum> [dig]\n"
+		"         <278>  Takes you to the given room vnum, with the\n"
+		"         <278>  dig argument a new room will be created if none exists.\n"
+		"\n"
+		"         <178>#map goto <name> <exits> <desc> <area> <note> <terrain>\n"
+		"         <278>  Takes you to the given room name, if you provide exits those\n"
+		"         <278>  must match.\n"
+		"\n"
 		"         <178>#map info\n"
 		"         <278>  Gives information about the map and room you are in.\n"
 		"\n"
 		"         <178>#map insert <direction> [roomflag]\n"
-		"         <278>  Insert a room in the given\n"
-		"         <278>  direction. Most useful for inserting void rooms.\n"
+		"         <278>  Insert a room in the given direction. Most useful for inserting\n"
+		"         <278>  void rooms.\n"
 		"\n"
 		"         <178>#map jump <x> <y> <z>\n"
 		"         <278>  Jump to the given coordinate, which is relative\n"
@@ -1922,10 +2025,18 @@ struct help_type help_table[] =
 		"         <278>  argument and a valid direction is given the link is two ways.\n"
 		"\n"
 		"         <178>#map list <name> <exits> <desc> <area> <note> <terrain>\n"
-		"         <278>  Lists all matching rooms and their distance.\n"
-		"\n"
-		"         <278>  Use {variable} {<variable>} to save the output to a variable.\n"
-		"         <278>  {roomname} {<name>}, {roomarea} {<area>}, etc, are valid too.\n"
+		"         <278>  Lists all matching rooms and their distance. The following\n"
+		"         <278>  search keywords are supported.\n"
+		"\n"
+		"         <278>  {roomarea}    <arg> will list rooms with matching area name.\n"
+		"         <278>  {roomdesc}    <arg> will list rooms with matching room desc.\n"
+		"         <278>  {roomexits}   <arg> will list rooms with identical room exits.\n"
+		"         <278>  {roomflag}    <arg> will list rooms with matching room flags.\n"
+		"         <278>  {roomid}      <arg> will list rooms with identical id name.\n"
+		"         <278>  {roomname}    <arg> will list rooms with matching room name.\n"
+		"         <278>  {roomnote}    <arg> will list rooms with matching room note.\n"
+		"         <278>  {roomterrain} <arg> will list rooms with matching room terrain.\n"
+		"         <278>  {variable}    <arg> will save the output to given variable.\n"
 		"\n"
 		"         <178>#map map <rows> <cols> <append|overwrite|list|variable> <name>\n"
 		"         <278>  Display a drawing of the map of the given height and width.\n"
@@ -1992,21 +2103,20 @@ struct help_type help_table[] =
 		"         <278>  when walking around. Useful for mapping mazes.\n"
 		"\n"
 		"         <178>#map run <room name> [delay]\n"
-		"         <278>  Calculates the shortest path to the\n"
-		"         <278>  destination and walks you there. The delay is optional and\n"
-		"         <278>  requires using braces. Besides the room name a list of\n"
-		"         <278>  exits can be provided for more precise matching.\n"
+		"         <278>  Calculates the shortest path to the destination and walks you\n"
+		"         <278>  there. The delay is optional and requires using braces. Besides\n"
+		"         <278>  the room name a list of exits can be provided for more precise\n"
+		"         <278>  matching.\n"
 		"\n"
 		"         <178>#map set <option> <value> [vnum]\n"
-		"         <278>  Set a map value for your current\n"
-		"         <278>  room, or given room if a room vnum is provided.\n"
+		"         <278>  Set a map value for your current room, or given room if a room\n"
+		"         <278>  vnum is provided.\n"
 		"\n"
 		"         <178>#map travel <direction> <delay>\n"
-		"         <278>  Follows the direction until a dead end\n"
-		"         <278>  or an intersection is found. Use braces around the direction\n"
-		"         <278>  if you use the delay, which will add the given delay between\n"
-		"         <278>  movements\n"
-		"         <278>  Use #undelay PATH %* to abort delayed movement.\n"
+		"         <278>  Follows the direction until a dead end or an intersection is\n"
+		"         <278>  found. Use braces around the direction if you use the delay,\n"
+		"         <278>  which will add the given delay between movements.\n"
+		"         <278>  Use #path stop to stop a delayed run.\n"
 		"\n"
 		"         <178>#map undo\n"
 		"         <278>  Will undo your last move. If this created a room or a link\n"
@@ -2017,18 +2127,16 @@ struct help_type help_table[] =
 		"         <278>  Exact opposite of the insert command.\n"
 		"\n"
 		"         <178>#map unlink <direction> [both]\n"
-		"         <278>  Will remove the exit, this isn't two\n"
-		"         <278>  way so you can have the map properly display no exit rooms and\n"
-		"         <278>  mazes.\n"
+		"         <278>  Will remove the exit, this isn't two way so you can have the\n"
+		"         <278>  properly display no exit rooms and mazes.\n"
 		"         <278>  If you use the both argument the exit is removed two-ways.\n"
 		"\n"
 		"         <178>#map update\n"
 		"         <278>  Sets the vtmap to update within the next 0.1 seconds.\n"
 		"\n"
 		"         <178>#map vnum <low> [high]\n"
-		"         <278>  Change the room vnum to the given number, if\n"
-		"         <278>  a range is provided the first available room in that range\n"
-		"         <278>  is selected.\n"
+		"         <278>  Change the room vnum to the given number, if a range is\n"
+		"         <278>  provided the first available room in that range is selected.\n"
 		"\n"
 		"         <178>#map write <filename> [force]\n"
 		"         <278>  Will save the map, if you want to save a map to a .tin file\n"
@@ -2059,12 +2167,15 @@ struct help_type help_table[] =
 		"         -               2            integer subtraction\n"
 		"         <<              3            bitwise shift\n"
 		"         >>              3            bitwise shift\n"
+		"         ..              3            bitwise ellipsis\n"
 		"         >               4            logical greater than\n"
 		"         >=              4            logical greater than or equal\n"
 		"         <               4            logical less than\n"
 		"         <=              4            logical less than or equal\n"
 		"         ==              5            logical equal (can use regex)\n"
+		"         ===             5            logical equal (never regex)\n"
 		"         !=              5            logical not equal (can use regex)\n"
+		"         !==             5            logical not equal (never regex)\n"
 		"          &              6            bitwise and\n"
 		"          ^              7            bitwise xor\n"
 		"          |              8            bitwise or\n"
@@ -2470,20 +2581,44 @@ struct help_type help_table[] =
 
 		"<178>Command<278>: #port <178>{<278>option<178>} {<278>argument<178>}<278>\n"
 		"\n"
-		"         #port {init} {name} {port} {file}     Initilize a port session.\n"
-		"\n"
-		"         #port {call}       {address} {port}   Connect to a remote socket\n"
-		"         #port {color}      {color names}      Set the default color\n"
-		"         #port {dnd}                           Decline new connections\n"
-		"         #port {group}      {name} {group}     Assign a socket group\n"
-		"         #port {ignore}     {name}             Ignore a socket\n"
-		"         #port {info}                          Display your info\n"
-		"         #port {name}       {name}             Change socket name.\n"		
-		"         #port {prefix}     {text}             Set prefix before each message.\n"		
-		"         #port {send}       {name|all} {text}  Send data to socket\n"
-		"         #port {uninitialize}                  Unitialize the port session.\n"
-		"         #port {who}                           Show all connections\n"
-		"         #port {zap}        {name}             Close a connection\n"
+		"         <178>#port {init} {name} {port} {file}\n"
+		"         <278>  Initilize a port session.\n"
+		"\n"
+		"         <178>#port {call} {address} {port}\n"
+		"         <278>  Connect to a remote socket.\n"
+		"\n"
+		"         <178>#port {color} {color names}\n"
+		"         <278>  Set the default color of port messages.\n"
+		"\n"
+		"         <178>#port {dnd}\n"
+		"         <278>  Do Not Disturb. Decline new connections\n"
+		"\n"
+		"         <178>#port {group} {name} {group}\n"
+		"         <278>  Assign a socket group.\n"
+		"\n"
+		"         <178>#port {ignore} {name}\n"
+		"         <278>  Ignore a socket\n"
+		"\n"
+		"         <178>#port {info}\n"
+		"         <278>  Display information about the port session.\n"
+		"\n"
+		"         <178>#port {name} {name}\n"
+		"         <278>  Change socket name.\n"
+		"\n"
+		"         <178>#port {prefix} {text}\n"
+		"         <278>  Set prefix before each message.\n"
+		"\n"
+		"         <178>#port {send} {name|all} {text}\n"
+		"         <278>  Send data to socket\n"
+		"\n"
+		"         <178>#port {uninitialize}\n"
+		"         <278>  Unitialize the port session.\n"
+		"\n"
+		"         <178>#port {who}\n"
+		"         <278>  Show all connections\n"
+		"\n"
+		"         <178>#port {zap} {name}\n"
+		"         <278>  Close a connection\n"
 		"\n"
 		"         The port command is very similar to chat except that it creates a\n"
 		"         new session dedicated to receiving socket connections at the given\n"
@@ -2491,6 +2626,7 @@ struct help_type help_table[] =
 
 		"all chat run session sessionname snoop ssl zap"
 	},
+
 	{
 		"PROMPT",
 
@@ -2570,8 +2706,8 @@ struct help_type help_table[] =
 		"      %S match zero to any number of non spaces.\n"
 		"\n"
 		"      %? match zero or one character.\n"
-		"      %. match one character.\n"
-		"      %+ match one to any number of characters.\n"
+		"      %. match one character. (<118>do not use<278>)\n"
+		"      %+ match one to any number of characters. (<118>do not use<278>)\n"
 		"      %* match zero to any number of characters.\n"
 		"\n"
 		"      %i matching becomes case insensitive.\n"
@@ -2644,35 +2780,53 @@ struct help_type help_table[] =
 
 		"<178>Command<278>: #scan <178>{<278>abort<178>|<278>csv<178><178>|<278>tsv<178><178>|<278>txt<178>} {<278>filename<178>}<278>\n"
 		"\n"
-		"         The scan txt <filename> command reads in a file and sends its content\n"
-		"         to the screen as if it was send by a mud. After using scan you can use\n"
-		"         page-up and down to view the file.\n"
+		"         The scan command is a file loading utility.\n"
+		"\n"
+		"         <178>#scan {abort}\n"
+		"         <278>  This command must be called from with a SCAN event and will\n"
+		"         <278>  abort the scan if one is in progress.\n"
+		"\n"
+		"         <178>#scan {csv} <filename>\n"
+		"         <278>  The scan csv command reads in a comma separated value file\n"
+		"           without printing the content to the screen. Instead it triggers one\n"
+		"           of two events.\n"
+		"\n"
+		"           The SCAN CSV HEADER event is triggered on the first line of the csv\n"
+		"           file. The SCAN CSV LINE event is triggered on the second and each\n"
+		"           subsequent line of the csv file. The %0 argument contains the entire\n"
+		"           line, with  %1 containing the first value, %2 the second value, etc,\n"
+		"           all the way up to %99.\n"
+		"\n"
+		"           Values containing spaces must be surrounded with quotes, keep in mind\n"
+		"           newlines within quotes are not supported. Use two quotes to print one\n"
+		"           literal quote character.\n"
+		"\n"
+		"         <178>#scan {tsv} <filename>\n"
+		"\n"
+		"         <278>  The scan tsv <filename> command reads in a tab separated value file\n"
+		"           without printing the content to the screen. Instead it triggers the\n"
+		"           SCAN TSV HEADER event for the first line and SCAN TSV LINE for all\n"
+		"           subsequent lines.\n"
 		"\n"
-		"         This command is useful to convert ansi color files to html or viewing\n"
-		"         raw log files.\n"
+		"         <178>#scan {file} <filename> {commands}\n"
 		"\n"
-		"         Actions, highlights, and substitutions will trigger as normal, and it\n"
-		"         is possible to create an action to execute #scan abort to prematurely\n"
-		"         stop the scan.\n"
+		"         <278>  The scan file command reads the given files and executes the\n"
+		"            commands argument. &0 contains the raw content of the file and\n"
+		"            &1 contains the plain content. &2 contains the raw byte size of the\n"
+		"            file and &3 the plain byte size. &5 contains the line count.\n"
 		"\n"
-		"         The scan csv <filename> command reads in a comma separated value file\n"
-		"         without printing the content to the screen. Instead it triggers one of\n"
-		"         two events.\n"
+		"         <178>#scan {txt} <filename>\n"
 		"\n"
-		"         The SCAN CSV HEADER event is triggered on the first line of the csv\n"
-		"         file. The SCAN CSV LINE event is triggered on the second and subsequent\n"
-		"         lines of the csv file. The %0 argument contains the entire line, with\n"
-		"         %1 containing the first value, %2 the second value, etc, all the way up\n"
-		"         to %99.\n"
+		"         <278>  The scan txt <filename> command reads in a file and sends its content\n"
+		"           to the screen as if it was send by a mud. After using scan you can\n"
+		"           use page-up and down to view the file.\n"
 		"\n"
-		"         Values containing spaces must be surrounded with quotes, keep in mind\n"
-		"         newlines within quotes are not supported. Use two quotes to print one\n"
-		"         literal quote character.\n"
+		"           This command is useful to convert ansi color files to html or viewing\n"
+		"           raw log files.\n"
 		"\n"
-		"         The scan tsv <filename> command reads in a tab separated value file\n"
-		"         without printing the content to the screen. Instead it triggers the\n"
-		"         SCAN TSV HEADER event for the first line and SCAN TSV LINE for all\n"
-		"         subsequent lines.\n",
+		"           Actions, highlights, and substitutions will trigger as normal, and it\n"
+		"           is possible to create an action to execute #scan abort to prematurely\n"
+		"           stop the scan.\n",
 
 		"read textin"
 	},
@@ -2832,6 +2986,32 @@ struct help_type help_table[] =
 
 		"all port run sessionname snoop ssl zap"
 	},
+
+	{
+		"SESSIONNAME",
+
+		"<178>Syntax<278>: #[sessionname] <178>{<278>commands<178>}<278>\n"
+		"\n"
+		"You can create multiple sessions with the #session command. By default only one\n"
+		"session is active, meaning commands you input are executed in the active\n"
+		"session. While all sessions receive output, only output sent to the active\n"
+		"session is displayed.\n"
+		"\n"
+		"When you create a session with the #session command you must specify a session\n"
+		"name, the session name, prepended with a hashtag, can be used to activate the\n"
+		"session when used without an argument. If an argument is given it will be\n"
+		"executed by that session as a command, the session will not be activated.\n"
+		"\n"
+		"<178>Example<278>: #ses one mymud.com 23;#ses two mymud.com 23;#one;#two grin\n"
+		"\n"
+		"This will create two sessions, the session that was created last (two in this\n"
+		"case) will be automatically activated upon creation. Using #one, session one is\n"
+		"activated. Using #two grin, the grin social will be executed by session two,\n"
+		"session one will remain the active session.\n",
+		
+		"all port run session snoop ssl zap"
+	},
+
 	{
 		"SHOWME",
 
@@ -2882,39 +3062,44 @@ struct help_type help_table[] =
 	{
 		"SPLIT",
 
-		"<178>Command<278>: #split <178>{<278>top status bar height<178>} {<278>bottom status bar height<178>}<278>\n"
+		"<178>Command<278>: #split <178>{<278>top bar<178>} {<278>bottom bar<178>}\n"
+		"<178>Command<278>: #split <178>{<278><square><178>}\n"
 		"\n"
 		"         This option requires for your terminal to support VT100 emulation.\n"
 		"\n"
-		"         #split allows the creation of an input line, a bottom status bar, a\n"
-		"         top status bar, and a scrolling text region.\n"
+		"         #split allows the creation of a top status bar, a scrolling region, a\n"
+		"         bottom status bar, and an input line.\n"
+		"\n"
+		"         <178>--top status bar--------\n"
+		"\n"
+		"         <278>  scrolling region\n"
+		"\n"
+		"         <178>--bottom status bar----------\n"
+		"         <278>  input line\n"
 		"\n"
 		"         By default the bottom status bar is filled with dashes --- and\n"
 		"         subsequently it is also known as the split line. The scrolling\n"
-		"         text region is also known as the main screen and this is where\n"
-		"         all incoming text is displayed by default.\n"
+		"         region is also known as the main screen and this is where all\n"
+		"         incoming text is displayed by default.\n"
 		"\n"
 		"         If you use #split without an argument it will set the height of the\n"
 		"         top status bar to 0 lines and the bottom status bar to 1 line.\n"
 		"\n"
 		"         If you use #split with one argument it will set the height of the top\n"
-		"         status bar to 0 lines and the bottom status bar will be set to 1 line.\n"
+		"         status bar to the given number of lines and the bottom status bar will\n"
+		"         be set to 1 line.\n"
 		"\n"
 		"         If you use two arguments the first argument is the height of the top\n"
 		"         status bar and the second argument the height of the bottom status bar.\n"
 		"\n"
-		"         <178>--top status bar--------\n"
-		"\n"
-		"         <278>  scrolling text region\n"
-		"\n"
-		"         <178>--bottom status bar----------\n"
-		"         <278>  input line\n"
+		"         The third and fourth argument are optional and tintin will interpret\n"
+		"         four arguments as a square argument existing of two coordinates\n"
+		"         defining the upper left corner and bottom right corner of the\n"
+		"         scrolling region.\n"
 		"\n"
 		"<178>Example<278>: #split 0 0\n"
-		"         If tintin has determined that you have a screen of 30 rows, it will\n"
-		"         set the scroll text region line 1 to line 29. With this example you\n"
-		"         will have no status bars, but you will have an input bar, meaning\n"
-		"         that if there is incoming text it won't overwrite what you are typing.\n"
+		"         This will create a split screen with just a scrolling regino and an\n"
+		"         input line. Great for the minimalist.\n"
 		"\n"
 		"<178>Comment<278>: You can display text on the split line(s) with the #prompt and\n"
 		"         #showme {line} {row} commands.\n"
@@ -2952,7 +3137,7 @@ struct help_type help_table[] =
 		"         #switch {expression} {commands}\n"
 		"         #while {expression} {commands}\n",
 
-		"break case continue default else elseif foreach if loop parse return switch while"
+		"commands help info"
 	},
 	{
 		"SUBSTITUTE",
@@ -2991,15 +3176,13 @@ struct help_type help_table[] =
 	{
 		"SUSPEND",
 
-		"<178>Command<278>: #suspend\n"
+		"<178>Command<278>: #cursor suspend\n"
 		"\n"
-		"         Temporarily suspends tintin and returns you to your shell.  The\n"
-		"         effect of this command is exactly as if you had typed control-z.\n"
-		"         To return to tintin, type 'fg' at the shell prompt.\n"
+		"         Temporarily suspends tintin and returns you to your shell.  To\n"
+		"         return to tintin, type 'fg' at the shell prompt.\n"
 		"\n"
 		"         While suspended your tintin sessions will freeze. To keep a\n"
-		"         suspended session running use the screen utility program and\n"
-		"         have it detach the session.\n",
+		"         suspended session running use the #detach command.\n",
 
 		"end"
 	},
@@ -3030,7 +3213,7 @@ struct help_type help_table[] =
 		"\n"
 		"         Executes the command specified as a shell command.\n",
 
-		"script run"
+		"detach script run"
 	},
 	{
 		"TAB",

+ 2 - 2
src/history.c

@@ -71,7 +71,7 @@ void add_line_history(struct session *ses, char *line)
 
 	root = ses->list[LIST_HISTORY];
 
-	if (HAS_BIT(root->flags, LIST_FLAG_IGNORE) || gtd->ignore_level)
+	if (HAS_BIT(root->flags, LIST_FLAG_IGNORE) || gtd->level->ignore)
 	{
 		return;
 	}
@@ -204,7 +204,7 @@ DO_HISTORY(history_size)
 {
 	if (atoi(arg) < 1 || atoi(arg) > 100000)
 	{
-		tintin_printf(ses, "#HISTORY SIZE: PROVIDE A NUMBER BETWEEN 1 and 100,000");
+		show_error(ses, LIST_COMMAND, "#ERROR: #HISTORY SIZE: PROVIDE A NUMBER BETWEEN 1 and 100,000");
 	}
 	else
 	{

+ 328 - 284
src/input.c

@@ -28,17 +28,56 @@
 
 void process_input(void)
 {
+	char input[STRING_SIZE];
+	int len, out;
+
+	push_call("process_input(void)");
+
+	if (gtd->detach_port)
+	{
+		if (gtd->detach_sock)
+		{
+			len = read(gtd->detach_sock, input, 1);
+		}
+		else
+		{
+			printf("process_input: error?\n");
+		}
+	}
+	else
+	{
+		len = read(0, input, 1);
+	}
+
+	input[len] = 0;
+
+	if (gtd->attach_sock)
+	{
+		out = write(gtd->attach_sock, input, 1);
+
+		if (out >= 0)
+		{
+			pop_call();
+			return;
+		}
+
+		gtd->attach_sock = close(gtd->attach_sock);
+
+		show_message(gtd->ses, LIST_COMMAND, "#ATTACH: WRITE ERROR: UNATTACHING.");
+	}
+
 	if (HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_SGA) && !HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_ECHO))
 	{
-		read_key();
+		read_key(input, len);
 	}
 	else
 	{
-		read_line();
+		read_line(input, len);
 	}
 
 	if (!HAS_BIT(gtd->flags, TINTIN_FLAG_PROCESSINPUT))
 	{
+		pop_call();
 		return;
 	}
 
@@ -74,6 +113,7 @@ void process_input(void)
 
 	if (check_all_events(gtd->ses, SUB_ARG|SUB_SEC, 0, 1, "CATCH RECEIVED INPUT", gtd->input_buf) == 1)
 	{
+		pop_call();
 		return;
 	}
 
@@ -92,206 +132,38 @@ void process_input(void)
 	}
 
 	gtd->input_buf[0] = 0;
-}
 
-void read_line()
-{
-	char buffer[STRING_SIZE];
-	struct listnode *node;
-	struct listroot *root;
-	int len, cnt, size, match, val[3], width, index;
+	fflush(NULL);
 
-	gtd->input_buf[gtd->input_len] = 0;
+	pop_call();
+	return;
+}
 
-	len = read(0, buffer, 1);
+void read_line(char *input, int len)
+{
+	int size, width, index;
 
-	buffer[len] = 0;
+//	gtd->input_buf[gtd->input_len] = 0;
 
 	if (HAS_BIT(gtd->ses->flags, SES_FLAG_CONVERTMETA))
 	{
-		convert_meta(buffer, &gtd->macro_buf[strlen(gtd->macro_buf)], FALSE);
+		convert_meta(input, &gtd->macro_buf[strlen(gtd->macro_buf)], FALSE);
 	}
 	else if (HAS_BIT(gtd->flags, TINTIN_FLAG_CONVERTMETACHAR))
 	{
-		convert_meta(buffer, &gtd->macro_buf[strlen(gtd->macro_buf)], TRUE);
+		convert_meta(input, &gtd->macro_buf[strlen(gtd->macro_buf)], TRUE);
 	}
 	else
 	{
-		strcat(gtd->macro_buf, buffer);
+		strcat(gtd->macro_buf, input);
 	}
 
-	if (!HAS_BIT(gtd->ses->flags, SES_FLAG_CONVERTMETA))
+	if (check_key(input, len))
 	{
-		match = 0;
-		root  = gtd->ses->list[LIST_MACRO];
-
-		if (!HAS_BIT(root->flags, LIST_FLAG_IGNORE))
-		{
-			for (root->update = 0 ; root->update < root->used ; root->update++)
-			{
-				node = root->list[root->update];
-
-				if (*node->arg1 == '^' && gtd->input_len)
-				{
-					continue;
-				}
-				else if (!strcmp(gtd->macro_buf, node->arg3))
-				{
-					script_driver(gtd->ses, LIST_MACRO, node->arg2);
-
-					gtd->macro_buf[0] = 0;
-
-					return;
-				}
-				else if (!strncmp(gtd->macro_buf, node->arg3, strlen(gtd->macro_buf)))
-				{
-					match = 1;
-				}
-			}
-		}
-
-		for (cnt = 0 ; *cursor_table[cnt].fun != NULL ; cnt++)
-		{
-			if (*cursor_table[cnt].code)
-			{
-				if (!strcmp(gtd->macro_buf, cursor_table[cnt].code))
-				{
-					cursor_table[cnt].fun(gtd->ses, "");
-					gtd->macro_buf[0] = 0;
-
-					return;
-				}
-				else if (!strncmp(gtd->macro_buf, cursor_table[cnt].code, strlen(gtd->macro_buf)))
-				{
-					match = 1;
-				}
-			}
-		}
-
-		if (match)
-		{
-			return;
-		}
-
-		if (gtd->macro_buf[0] == ASCII_ESC)
-		{
-			if (gtd->macro_buf[1] == '[')
-			{
-				if (gtd->macro_buf[2] == '<')
-				{
-					val[0] = val[1] = val[2] = cnt = buffer[0] = 0;
-
-					for (len = 3 ; gtd->macro_buf[len] ; len++)
-					{
-						if (isdigit(gtd->macro_buf[len]))
-						{
-							cat_sprintf(buffer, "%c", gtd->macro_buf[len]);
-						}
-						else
-						{
-							switch (gtd->macro_buf[len])
-							{
-								case ';':
-									val[cnt++] = get_number(gtd->ses, buffer);
-									buffer[0] = 0;
-									break;
-
-								case 'm':
-								case 'M':
-									val[cnt++] = get_number(gtd->ses, buffer);
-									mouse_handler(gtd->ses, val[0], val[2], val[1], gtd->macro_buf[len]); // swap x y to row col
-									gtd->macro_buf[0] = 0;
-									return;
-
-								default:
-									printf("unknownmouse input error (%s)\n", gtd->macro_buf);
-									gtd->macro_buf[0] = 0;
-									return;
-							}
-						}
-					}
-					return;
-				}
-
-				if (gtd->macro_buf[2] >= '0' && gtd->macro_buf[2] <= '9')
-				{
-					val[0] = val[1] = val[2] = cnt = buffer[0] = 0;
-
-					for (len = 2 ; gtd->macro_buf[len] ; len++)
-					{
-						if (isdigit(gtd->macro_buf[len]))
-						{
-							cat_sprintf(buffer, "%c", gtd->macro_buf[len]);
-						}
-						else
-						{
-							switch (gtd->macro_buf[len])
-							{
-								case '-':
-									if (buffer[0] == 0)
-									{
-										strcat(buffer, "-");
-									}
-									else
-									{
-										tintin_printf2(NULL, "\e[1;31merror: bad csi input (%s)\n", &gtd->macro_buf[1]);
-										gtd->macro_buf[0] = 0;
-										continue;
-									}
-									break;
-								case ';':
-									val[cnt++] = get_number(gtd->ses, buffer);
-									buffer[0] = 0;
-									break;
-
-								case 't':
-									val[cnt++] = get_number(gtd->ses, buffer);
-									csit_handler(val[0], val[1], val[2]);
-									gtd->macro_buf[0] = 0;
-									return;
-
-								default:
-									goto end_of_loop;
-							}
-						}
-					}
-					return;
-				}
-			}
-			else if (gtd->macro_buf[1] == ']')
-			{
-				switch (gtd->macro_buf[2])
-				{
-					case 'L':
-					case 'l':
-						for (len = 3 ; gtd->macro_buf[len] ; len++)
-						{
-							if (gtd->macro_buf[len] < 32)
-							{
-								buffer[len - 3] = 0;
-								osc_handler(gtd->macro_buf[2], buffer);
-								gtd->macro_buf[0] = 0;
-								return;
-							}
-							else
-							{
-								buffer[len - 3 ] = gtd->macro_buf[len];
-							}
-						}
-						break;
-
-					default:
-						printf("\e[1;31merror: unknown osc input (%s)\n", gtd->macro_buf);
-						gtd->macro_buf[0] = 0;
-						return;
-				}
-			}
-		}
+		return;
 	}
 
-	end_of_loop:
-
-	if (HAS_BIT(gtd->ses->flags, SES_FLAG_UTF8) && is_utf8_head(gtd->macro_buf))
+	if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(gtd->macro_buf))
 	{
 		if (get_utf8_size(gtd->macro_buf) == 1)
 		{
@@ -301,9 +173,9 @@ void read_line()
 
 	if (gtd->macro_buf[0] == ASCII_ESC)
 	{
-		strcpy(buffer, gtd->macro_buf);
+		strcpy(input, gtd->macro_buf);
 
-		convert_meta(buffer, gtd->macro_buf, FALSE);
+		convert_meta(input, gtd->macro_buf, FALSE);
 	}
 
 	get_utf8_index(gtd->macro_buf, &index);
@@ -328,7 +200,7 @@ void read_line()
 				break;
 
 			default:
-				if (HAS_BIT(gtd->ses->flags, SES_FLAG_UTF8) && gtd->macro_buf[0] & 128 && gtd->macro_buf[1])
+				if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8) && gtd->macro_buf[0] & 128 && gtd->macro_buf[1])
 				{
 					size = get_utf8_width(gtd->macro_buf, &width);
 
@@ -399,67 +271,33 @@ void read_line()
 	}
 }
 
-void read_key(void)
+void read_key(char *input, int len)
 {
-	char buffer[BUFFER_SIZE];
-	struct listnode *node;
-	struct listroot *root;
-	int len, cnt, match;
+	int cnt;
 
 	if (gtd->input_buf[0] == gtd->tintin_char)
 	{
-		read_line();
+		read_line(input, len);
 
 		return;
 	}
 
-	len = read(0, buffer, 1);
-
-	buffer[len] = 0;
-
-	if (HAS_BIT(gtd->ses->flags, SES_FLAG_CONVERTMETA) || HAS_BIT(gtd->flags, TINTIN_FLAG_CONVERTMETACHAR))
+	if (HAS_BIT(gtd->ses->flags, SES_FLAG_CONVERTMETA))
+	{
+		convert_meta(input, &gtd->macro_buf[strlen(gtd->macro_buf)], FALSE);
+	}
+	else if (HAS_BIT(gtd->flags, TINTIN_FLAG_CONVERTMETACHAR))
 	{
-		convert_meta(buffer, &gtd->macro_buf[strlen(gtd->macro_buf)], FALSE);
+		convert_meta(input, &gtd->macro_buf[strlen(gtd->macro_buf)], TRUE);
 	}
 	else
 	{
-		strcat(gtd->macro_buf, buffer);
+		strcat(gtd->macro_buf, input);
 	}
 
-	if (!HAS_BIT(gtd->ses->flags, SES_FLAG_CONVERTMETA))
+	if (check_key(input, len))
 	{
-		match = 0;
-
-		root  = gtd->ses->list[LIST_MACRO];
-
-		if (!HAS_BIT(root->flags, LIST_FLAG_IGNORE))
-		{
-			for (root->update = 0 ; root->update < root->used ; root->update++)
-			{
-				node = root->list[root->update];
-
-				if (*node->arg1 == '^' && gtd->input_buf[0])
-				{
-					continue;
-				}
-				else if (!strcmp(gtd->macro_buf, node->arg3))
-				{
-					script_driver(gtd->ses, LIST_MACRO, node->arg2);
-
-					gtd->macro_buf[0] = 0;
-					return;
-				}
-				else if (!strncmp(gtd->macro_buf, node->arg3, strlen(gtd->macro_buf)))
-				{
-					match = 1;
-				}
-			}
-		}
-
-		if (match)
-		{
-			return;
-		}
+		return;
 	}
 
 	for (cnt = 0 ; gtd->macro_buf[cnt] ; cnt++)
@@ -487,11 +325,11 @@ void read_key(void)
 				{
 					if (gtd->input_len != gtd->input_cur)
 					{
-						printf("\e[1@%c", gtd->macro_buf[cnt]);
+						print_stdout("\e[1@%c", gtd->macro_buf[cnt]);
 					}
 					else
 					{
-						printf("%c", gtd->macro_buf[cnt]);
+						print_stdout("%c", gtd->macro_buf[cnt]);
 					}
 					gtd->input_buf[0] = gtd->tintin_char;
 					gtd->input_buf[1] = 0;
@@ -512,18 +350,221 @@ void read_key(void)
 	}
 }
 
+int check_key(char *input, int len)
+{
+	char buf[BUFFER_SIZE];
+	struct listroot *root;
+	struct listnode *node;
+	int cnt, val[3];
+
+	push_call("check_key(%p,%d)",input,len);
+
+	if (!HAS_BIT(gtd->ses->flags, SES_FLAG_CONVERTMETA))
+	{
+		root  = gtd->ses->list[LIST_MACRO];
+
+		if (!HAS_BIT(root->flags, LIST_FLAG_IGNORE))
+		{
+			for (root->update = 0 ; root->update < root->used ; root->update++)
+			{
+				node = root->list[root->update];
+
+				if (*node->arg1 == '^' && gtd->input_len)
+				{
+					continue;
+				}
+				else if (!strcmp(gtd->macro_buf, node->arg3))
+				{
+					strcpy(buf, node->arg2);
+
+					if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+					{
+						delete_node_list(gtd->ses, LIST_MACRO, node);
+					}
+
+					script_driver(gtd->ses, LIST_MACRO, buf);
+
+					gtd->macro_buf[0] = 0;
+					pop_call();
+					return TRUE;
+				}
+				else if (!strncmp(gtd->macro_buf, node->arg3, strlen(gtd->macro_buf)))
+				{
+					pop_call();
+					return TRUE;
+				}
+			}
+		}
+
+		if (!HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_SGA) || HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_ECHO) || gtd->input_buf[0] == gtd->tintin_char)
+		{
+			for (cnt = 0 ; *cursor_table[cnt].fun != NULL ; cnt++)
+			{
+				if (*cursor_table[cnt].code)
+				{
+					if (!strcmp(gtd->macro_buf, cursor_table[cnt].code))
+					{
+						cursor_table[cnt].fun(gtd->ses, "");
+						gtd->macro_buf[0] = 0;
+
+						pop_call();
+						return TRUE;
+					}
+					else if (!strncmp(gtd->macro_buf, cursor_table[cnt].code, strlen(gtd->macro_buf)))
+					{
+						pop_call();
+						return TRUE;
+					}
+				}
+			}
+		}
+
+		if (gtd->macro_buf[0] == ASCII_ESC)
+		{
+			if (gtd->macro_buf[1] == '[')
+			{
+				if (gtd->macro_buf[2] == '<' && !HAS_BIT(gtd->ses->list[LIST_BUTTON]->flags, LIST_FLAG_IGNORE))
+				{
+					val[0] = val[1] = val[2] = cnt = input[0] = 0;
+
+					for (len = 3 ; gtd->macro_buf[len] ; len++)
+					{
+						if (isdigit(gtd->macro_buf[len]))
+						{
+							cat_sprintf(input, "%c", gtd->macro_buf[len]);
+						}
+						else
+						{
+							switch (gtd->macro_buf[len])
+							{
+								case ';':
+									val[cnt++] = get_number(gtd->ses, input);
+									input[0] = 0;
+									break;
+
+								case 'm':
+								case 'M':
+									val[cnt++] = get_number(gtd->ses, input);
+									mouse_handler(gtd->ses, val[0], val[2], val[1], gtd->macro_buf[len]); // swap x y to row col
+									gtd->macro_buf[0] = 0;
+									pop_call();
+									return TRUE;
+
+								default:
+									print_stdout("unknownmouse input error (%s)\n", gtd->macro_buf);
+									gtd->macro_buf[0] = 0;
+									pop_call();
+									return TRUE;
+							}
+						}
+					}
+					pop_call();
+					return TRUE;
+				}
+				else if (gtd->macro_buf[2] >= '0' && gtd->macro_buf[2] <= '9')
+				{
+					val[0] = val[1] = val[2] = cnt = input[0] = 0;
+
+					for (len = 2 ; gtd->macro_buf[len] ; len++)
+					{
+						if (isdigit(gtd->macro_buf[len]))
+						{
+							cat_sprintf(input, "%c", gtd->macro_buf[len]);
+						}
+						else
+						{
+							switch (gtd->macro_buf[len])
+							{
+								case '-':
+									if (input[0] == 0)
+									{
+										strcat(input, "-");
+									}
+									else
+									{
+										tintin_printf2(NULL, "\e[1;31merror: bad csi input (%s)\n", &gtd->macro_buf[1]);
+										gtd->macro_buf[0] = 0;
+										continue;
+									}
+									break;
+								case ';':
+									val[cnt++] = get_number(gtd->ses, input);
+									input[0] = 0;
+									break;
+
+								case 't':
+									val[cnt++] = get_number(gtd->ses, input);
+									csit_handler(val[0], val[1], val[2]);
+									gtd->macro_buf[0] = 0;
+									pop_call();
+									return TRUE;
+
+								default:
+									pop_call();
+									return FALSE;
+							}
+						}
+					}
+					pop_call();
+					return TRUE;
+				}
+				else if (gtd->macro_buf[2] == 0)
+				{
+					pop_call();
+					return TRUE;
+				}
+			}
+			else if (gtd->macro_buf[1] == ']')
+			{
+				switch (gtd->macro_buf[2])
+				{
+					case 'L':
+					case 'l':
+						for (len = 3 ; gtd->macro_buf[len] ; len++)
+						{
+							if (gtd->macro_buf[len] < 32)
+							{
+								input[len - 3] = 0;
+								osc_handler(gtd->macro_buf[2], input);
+								gtd->macro_buf[0] = 0;
+								pop_call();
+								return TRUE;
+							}
+							else
+							{
+								input[len - 3 ] = gtd->macro_buf[len];
+							}
+						}
+						break;
+
+					default:
+						print_stdout("\e[1;31merror: unknown osc input (%s)\n", gtd->macro_buf);
+						gtd->macro_buf[0] = 0;
+						pop_call();
+						return TRUE;
+				}
+			}
+			else if (gtd->macro_buf[1] == 0)
+			{
+				pop_call();
+				return TRUE;
+			}
+		}
+	}
+	pop_call();
+	return FALSE;
+}
+
 void convert_meta(char *input, char *output, int eol)
 {
 	char *pti, *pto;
 
 	push_call("convert_meta(%p,%p,%d)",input,output,eol);
 
-	DEL_BIT(gtd->flags, TINTIN_FLAG_CONVERTMETACHAR);
-
 	pti = input;
 	pto = output;
 
-	while (*pti)
+	while (*pti && pti - input < BUFFER_SIZE / 2)
 	{
 		switch (*pti)
 		{
@@ -566,6 +607,15 @@ void convert_meta(char *input, char *output, int eol)
 				break;
 
 			case ASCII_CR:
+				if (HAS_BIT(gtd->flags, TINTIN_FLAG_CONVERTMETACHAR))
+				{
+					*pto++ = '\\';
+					*pto++ = 'r';
+					*pto = 0;
+					DEL_BIT(gtd->flags, TINTIN_FLAG_CONVERTMETACHAR);
+					pop_call();
+					return;
+				}
 				if (eol)
 				{
 					*pto++ = '\\';
@@ -581,12 +631,23 @@ void convert_meta(char *input, char *output, int eol)
 				break;
 
 			case ASCII_LF:
+				if (HAS_BIT(gtd->flags, TINTIN_FLAG_CONVERTMETACHAR))
+				{
+					*pto++ = '\\';
+					*pto++ = 'n';
+					*pto = 0;
+					DEL_BIT(gtd->flags, TINTIN_FLAG_CONVERTMETACHAR);
+					pop_call();
+					return;
+				}
+
 				if (eol)
 				{
 					*pto++ = '\\';
 					*pto++ = 'n';
 				}
 				*pto++ = *pti++;
+
 				break;
 
 			default:
@@ -614,6 +675,8 @@ void convert_meta(char *input, char *output, int eol)
 	}
 	*pto = 0;
 
+	DEL_BIT(gtd->flags, TINTIN_FLAG_CONVERTMETACHAR);
+
 	pop_call();
 	return;
 }
@@ -634,69 +697,50 @@ char *str_convert_meta(char *input, int eol)
 
 void echo_command(struct session *ses, char *line)
 {
-	char buffer[STRING_SIZE], result[STRING_SIZE];
+	char buffer[BUFFER_SIZE], output[BUFFER_SIZE];
+
+	DEL_BIT(ses->telopts, TELOPT_FLAG_PROMPT);
 
-	if (HAS_BIT(ses->flags, SES_FLAG_SPLIT))
+	if (ses->check_output)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_ECHOCOMMAND))
-		{
-			sprintf(buffer, "%s%s\e[0m", ses->cmd_color, line);
-		}
-		else
-		{
-			sprintf(buffer, "\e[0m");
-		}
+		strcpy(output, ses->more_output);
+
+		process_mud_output(ses, buffer, FALSE);
 	}
 	else
 	{
-		sprintf(buffer, "%s", line);
+		strcpy(output, "");
 	}
 
-	/*
-		Deal with pending output
-	*/
-
-	if (ses->more_output[0])
+	if (!HAS_BIT(ses->flags, SES_FLAG_SPLIT))
 	{
-		if (ses->check_output)
-		{
-			strcpy(result, ses->more_output);
-			ses->more_output[0] = 0;
+		add_line_buffer(ses, line, -1);
 
-			process_mud_output(ses, result, FALSE);
-		}
+		return;
 	}
 
-	DEL_BIT(ses->telopts, TELOPT_FLAG_PROMPT);
-
-	if (HAS_BIT(ses->flags, SES_FLAG_SPLIT))
+	if (HAS_BIT(ses->flags, SES_FLAG_ECHOCOMMAND))
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_ECHOCOMMAND))
-		{
-			sprintf(result, "%s%s", ses->more_output, buffer);
-		}
-		else
+		sprintf(buffer, "%s%s\e[0m", ses->cmd_color, line);
+	}
+	else
+	{
+		if (strip_vt102_strlen(ses, output) == 0)
 		{
-			if (strip_vt102_strlen(ses, ses->more_output) == 0)
-			{
-				return;
-			}
-			sprintf(result, "%s", ses->more_output);
+			return;
 		}
+		sprintf(buffer, "\e[0m");
+	}
 
-		add_line_buffer(ses, buffer, -1);
-
-		gtd->scroll_level++;
+	if (ses->wrap == gtd->screen->cols)
+	{
+		gtd->level->scroll++;
 
-		tintin_printf2(ses, "%s", result);
+		tintin_printf2(ses, "%s%s", ses->scroll->input, buffer);
 
-		gtd->scroll_level--;
-	}
-	else
-	{
-		add_line_buffer(ses, buffer, -1);
-		add_line_screen(buffer);
+		gtd->level->scroll--;
 	}
+	add_line_buffer(ses, buffer, -1);
 }
 
 void input_printf(char *format, ...)
@@ -716,7 +760,7 @@ void input_printf(char *format, ...)
 	vsprintf(buf, format, args);
 	va_end(args);
 
-	printf("%s", buf);
+	print_stdout("%s", buf);
 }
 
 void modified_input(void)

+ 65 - 11
src/line.c

@@ -35,7 +35,18 @@ DO_COMMAND(do_line)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {<OPTION>} {argument}.");
+		tintin_header(ses, " LINE OPTIONS ");
+
+		for (cnt = 0 ; *line_table[cnt].fun != NULL ; cnt++)
+		{
+			if (*line_table[cnt].desc)
+			{
+				tintin_printf2(ses, "  [%-13s] %s", line_table[cnt].name, line_table[cnt].desc);
+			}
+		}
+		tintin_header(ses, "");
+
+		return ses;
 	}
 	else
 	{
@@ -72,11 +83,33 @@ DO_LINE(line_background)
 		return ses;
 	}
 
-	gtd->background_level++;
+	gtd->level->background++;
+
+	ses = script_driver(ses, LIST_COMMAND, arg1);
+
+	gtd->level->background--;
+
+	return ses;
+}
+
+DO_LINE(line_debug)
+{
+	char arg1[BUFFER_SIZE];
+
+	arg = get_arg_in_braces(ses, arg, arg1, GET_ALL);
+
+	if (*arg1 == 0)
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {DEBUG} {command}.");
+
+		return ses;
+	}
+
+	gtd->level->debug++;
 
 	ses = script_driver(ses, LIST_COMMAND, arg1);
 
-	gtd->background_level--;
+	gtd->level->debug--;
 
 	return ses;
 }
@@ -107,11 +140,11 @@ DO_LINE(line_ignore)
 		return ses;
 	}
 
-	gtd->ignore_level++;
+	gtd->level->ignore++;
 
 	ses = script_driver(ses, LIST_COMMAND, arg1);
 
-	gtd->ignore_level--;
+	gtd->level->ignore--;
 
 	return ses;
 }
@@ -314,6 +347,27 @@ DO_LINE(line_logverbatim)
 	return ses;
 }
 
+DO_LINE(line_oneshot)
+{
+	char arg1[BUFFER_SIZE];
+
+	arg = get_arg_in_braces(ses, arg, arg1, GET_ALL);
+
+	if (*arg1 == 0)
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {ONESHOT} {command}.");
+		
+		return ses;
+	}
+
+	gtd->level->oneshot++;
+
+	ses = script_driver(ses, LIST_COMMAND, arg1);
+
+	gtd->level->oneshot--;
+
+	return ses;
+}
 
 DO_LINE(line_quiet)
 {
@@ -328,11 +382,11 @@ DO_LINE(line_quiet)
 		return ses;
 	}
 
-	gtd->quiet_level++;
+	gtd->level->quiet++;
 
 	ses = script_driver(ses, LIST_COMMAND, arg1);
 
-	gtd->quiet_level--;
+	gtd->level->quiet--;
 
 	return ses;
 }
@@ -412,11 +466,11 @@ DO_LINE(line_verbatim)
 		return ses;
 	}
 
-	gtd->verbatim_level++;
+	gtd->level->verbatim++;
 
 	ses = parse_input(ses, arg1);
 
-	gtd->verbatim_level--;
+	gtd->level->verbatim--;
 
 	return ses;
 }
@@ -434,11 +488,11 @@ DO_LINE(line_verbose)
 		return ses;
 	}
 
-	gtd->verbose_level++;
+	gtd->level->verbose++;
 
 	ses = script_driver(ses, LIST_COMMAND, arg1);
 
-	gtd->verbose_level--;
+	gtd->level->verbose--;
 
 	return ses;
 }

+ 20 - 6
src/list.c

@@ -33,16 +33,29 @@ DO_COMMAND(do_list)
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
 	struct listroot *root;
 	struct listnode *node;
-	int cnt;
+	int index, cnt;
 
 	root = ses->list[LIST_VARIABLE];
 
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_NST, SUB_VAR|SUB_FUN);
 	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
 
-	if (*arg1 == 0 || *arg2 == 0)
+	if (*arg1 == 0)
+	{
+		tintin_header(ses, " LIST OPTIONS ");
+
+		for (index = 0 ; *array_table[index].fun ; index++)
+		{
+			if (array_table[index].desc && *array_table[index].name)
+			{
+				tintin_printf2(ses, "  [%-24s] %s", array_table[index].name, array_table[index].desc);
+			}
+		}
+		tintin_header(ses, "");
+	}
+	else if (*arg2 == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {variable} {ADD|CLE|CRE|DEL|FIN|GET|INS|SET|SIM|SIZ|SOR} {argument}");
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {variable} {option} {argument}");
 	}
 	else
 	{
@@ -64,7 +77,7 @@ DO_COMMAND(do_list)
 			{
 				node = set_nest_node(root, arg1, "");
 			}
-			array_table[cnt].array(ses, node, arg, arg1);
+			array_table[cnt].fun(ses, node, arg, arg1);
 		}
 	}
 	return ses;
@@ -479,11 +492,11 @@ DO_ARRAY(array_tokenize)
 
 	while (buf[i] != 0)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && buf[i] & 128 && buf[i+1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && buf[i] & 128 && buf[i+1] != 0)
 		{
 			i += sprintf(tmp, "%c%c", buf[i], buf[i+1]);
 		}
-		else if (HAS_BIT(ses->flags, SES_FLAG_UTF8) && is_utf8_head(&buf[i]))
+		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&buf[i]))
 		{
 			i += sprintf(tmp, "%.*s", get_utf8_size(&buf[i]), &buf[i]);
 		}
@@ -496,3 +509,4 @@ DO_ARRAY(array_tokenize)
 	}
 	return ses;
 }
+

+ 1 - 1
src/log.c

@@ -185,7 +185,7 @@ void write_html_header(struct session *ses, FILE *fp)
 		"</head>\n"
 		"<pre>\n"
 		"<span class='b49'><span class='d39'>\n",
-		HAS_BIT(gtd->ses->flags, SES_FLAG_UTF8) ? "utf-8" : HAS_BIT(ses->flags, SES_FLAG_BIG5) ? "big5" : "iso-8859-1");
+		HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8) ? "utf-8" : HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) ? "big5" : "iso-8859-1");
 
 	fputs(header, fp);
 }

+ 174 - 105
src/main.c

@@ -27,7 +27,6 @@
 #include "tintin.h"
 
 #include <signal.h>
-#include <sys/socket.h>
 
 /*************** globals ******************/
 
@@ -58,6 +57,8 @@ void winch_handler(int signal)
 			client_send_sb_naws(ses, 0, NULL);
 		}
 	}
+
+	winch_daemon();
 }
 
 
@@ -66,6 +67,11 @@ void abort_handler(int signal)
 	syserr_fatal(signal, "abort_handler");
 }
 
+void child_handler(int signal)
+{
+	// syserr_fatal(signal, "child_handler");
+}
+
 void interrupt_handler(int signal)
 {
 	if (gtd->ses->connect_retry > utime())
@@ -76,6 +82,12 @@ void interrupt_handler(int signal)
 	{
 		socket_printf(gtd->ses, 1, "%c", 4);
 	}
+	else if (gtd->attach_sock)
+	{
+		gtd->attach_sock = close(gtd->attach_sock);
+
+		show_message(gtd->ses, LIST_COMMAND, "#REDETACHING PROCESS TO {%s}", gtd->attach_file);
+	}
 	else
 	{
 		cursor_delete_or_exit(gtd->ses, "");
@@ -84,24 +96,20 @@ void interrupt_handler(int signal)
 
 void suspend_handler(int signal)
 {
-	printf("\e[r\e[%d;%dH", gtd->screen->rows, 1);
+	show_message(gtd->ses, LIST_COMMAND, "#SIGNAL: SIGTSTP");
 
-	fflush(NULL);
-
-	reset_terminal(gtd->ses);
-
-	kill(0, SIGSTOP);
-
-	init_terminal(gtd->ses);
+	if (gtd->attach_sock)
+	{
+		show_message(gtd->ses, LIST_COMMAND, "#DAEMON {%s} WANTS TO DETACH.", gtd->attach_file);
 
-	dirty_screen(gtd->ses);
+		gtd->attach_sock = close(gtd->attach_sock);
 
-	tintin_puts(NULL, "#RETURNING BACK TO TINTIN++.");
+		return;
+	}
 }
 
 void trap_handler(int signal)
 {
-
 	syserr_fatal(signal, "trap_handler");
 }
 
@@ -140,17 +148,23 @@ int main(int argc, char **argv)
 	{
 		syserr_fatal(-1, "signal SIGTERM");
 	}
+
+	if (signal(SIGCHLD, child_handler) == BADSIG)
+	{
+		syserr_fatal(-1, "signal SIGCHLD");
+	}
 /*
 	if (signal(SIGINT, interrupt_handler) == BADSIG)
 	{
 		syserr_fatal(-1, "signal SIGINT");
 	}
+*/
 
 	if (signal(SIGTSTP, suspend_handler) == BADSIG)
 	{
-		syserr_fatal(-1, "signal SIGSTOP");
+		syserr_fatal(-1, "signal SIGTSTP");
 	}
-*/
+
 	if (signal(SIGPIPE, pipe_handler) == BADSIG)
 	{
 		syserr_fatal(-1, "signal SIGPIPE");
@@ -174,25 +188,23 @@ int main(int argc, char **argv)
 
 	if (argc > 1)
 	{
-		while ((c = getopt(argc, argv, "a: e: G h r: s t: v")) != EOF)
+		while ((c = getopt(argc, argv, "a: e: G h r: R:: s t: T v")) != EOF)
 		{
 			switch (c)
 			{
 				case 'h':
-					init_tintin(STARTUP_FLAG_NOGREETING|STARTUP_FLAG_NORESET);
-
-					tintin_printf(NULL, "Usage: %s [OPTION]... [FILE]...", argv[0]);
-					tintin_printf(NULL, "");
-					tintin_printf(NULL, "  -a  Set argument for PROGRAM START event.");
-					tintin_printf(NULL, "  -e  Execute given command.");
-					tintin_printf(NULL, "  -G  Don't show the greeting screen.");
-					tintin_printf(NULL, "  -h  This help section.");
-					tintin_printf(NULL, "  -r  Read given file.");
-					tintin_printf(NULL, "  -s  Enable screen reader mode.");
-					tintin_printf(NULL, "  -t  Set given title.");
-					tintin_printf(NULL, "  -v  Enable verbose mode.");
-
-					reset_terminal(gtd->ses);
+					printf("Usage: %s [OPTION]... [FILE]...\n", argv[0]);
+					printf("\n");
+					printf("  -a  Set argument for PROGRAM START event.\n");
+					printf("  -e  Execute given command.\n");
+					printf("  -G  Don't show the greeting screen.\n");
+					printf("  -h  This help section.\n");
+					printf("  -r  Read given file.\n");
+					printf("  -s  Enable screen reader mode.\n");
+					printf("  -t  Set given title.\n");
+					printf("  -T  Don't set the default title.\n");
+					printf("  -v  Enable verbose mode.\n");
+
 					exit(1);
 					break;
 
@@ -229,7 +241,7 @@ int main(int argc, char **argv)
 
 		RESTRING(gtd->vars[2], argv[1]);
 
-		while ((c = getopt(argc, argv, "a: e: G h r: s t: v")) != EOF)
+		while ((c = getopt(argc, argv, "a: e: G h r: R:: s t: T v")) != EOF)
 		{
 			switch (c)
 			{
@@ -239,23 +251,35 @@ int main(int argc, char **argv)
 					break;
 
 				case 'e':
-					gtd->quiet_level++;
+					gtd->level->input++;
 					gtd->ses = script_driver(gtd->ses, LIST_COMMAND, optarg);
-					gtd->quiet_level--;
+					gtd->level->input--;
 					break;
 
 				case 'G':
 					break;
 
 				case 'r':
+					gtd->level->input++;
 					gtd->ses = do_read(gtd->ses, optarg);
+					gtd->level->input--;
+					break;
+
+				case 'R':
+					SET_BIT(gtd->flags, TINTIN_FLAG_DAEMONIZE);
+					daemon_attach(gtd->ses, optarg ? optarg : "");
 					break;
 
 				case 's':
 					break;
 
 				case 't':
-					printf("\e]0;%s\007", optarg);
+					SET_BIT(greeting, STARTUP_FLAG_NOTITLE);
+					print_stdout("\e]0;%s\007", optarg);
+					break;
+
+				case 'T':
+					SET_BIT(greeting, STARTUP_FLAG_NOTITLE);
 					break;
 
 				case 'v':
@@ -263,45 +287,65 @@ int main(int argc, char **argv)
 					break;
 
 				default:
-					tintin_printf(NULL, "Unknown option '%c'.", c);
-					break;
+//					tintin_printf2(NULL, "Unknown option '%c'.", c);
+					break;;
 			}
 		}
+	}
 
-		if (argc > 2)
-		{
-			RESTRING(gtd->vars[3], argv[2]);
+	if (!HAS_BIT(greeting, STARTUP_FLAG_NOTITLE))
+	{
+		do_screen(gts, "LOAD BOTH");
+		do_screen(gts, "SAVE BOTH");
+		do_screen(gts, "SET BOTH TinTin++");
+	}
 
-			for (i = 3 ; i <= optind ; i++)
-			{
-				RESTRING(gtd->vars[i+1], argv[i] ? argv[i] : "");
-			}
+	gtd->exec = strdup(argv[0]);
+
+	if (argc > 2)
+	{
+		RESTRING(gtd->vars[3], argv[2]);
 
-			arg[0] = 0;
+		for (i = 3 ; i <= optind ; i++)
+		{
+			RESTRING(gtd->vars[i+1], argv[i] ? argv[i] : "");
+		}
 
-			for (i = optind + 1 ; i < argc ; i++)
+		arg[0] = 0;
+
+		for (i = optind + 1 ; i < argc ; i++)
+		{
+			if (*arg)
 			{
-				if (*arg)
-				{
-					strcat(arg, " ");
-				}
-				strcat(arg, argv[i]);
-
-				if (i < 100)
-				{
-					RESTRING(gtd->vars[i+1], argv[i]);
-				}
+				strcat(arg, " ");
 			}
+			strcat(arg, argv[i]);
 
-			if (!HAS_BIT(greeting, STARTUP_FLAG_ARGUMENT))
+			if (i < 100)
 			{
-				RESTRING(gtd->vars[0], arg);
+				RESTRING(gtd->vars[i+1], argv[i]);
 			}
 		}
 
-		if (argv[optind] != NULL)
+		if (!HAS_BIT(greeting, STARTUP_FLAG_ARGUMENT))
 		{
+			RESTRING(gtd->vars[0], arg);
+		}
+	}
+
+	if (argv[optind] != NULL)
+	{
+		if (!strncasecmp(argv[optind], "telnet://", 9))
+		{
+			do_session(gts, argv[optind]);
+		}
+		else
+		{
+			gtd->level->input++;
+
 			gtd->ses = do_read(gtd->ses, argv[optind]);
+
+			gtd->level->input--;
 		}
 	}
 
@@ -316,31 +360,16 @@ void init_tintin(int greeting)
 {
 	int ref, index;
 
+	push_call("init_tintin(%d)",greeting);
+
 	gtd                 = (struct tintin_data *) calloc(1, sizeof(struct tintin_data));
 
-	gtd->ses = gts      = (struct session *) calloc(1, sizeof(struct session));
+	gtd->level          = (struct level_data *) calloc(1, sizeof(struct level_data));
+
 	gtd->str_size       = sizeof(struct str_data);
-	gtd->str_hash_size  = sizeof(struct str_hash_data);
 	gtd->buf            = str_alloc(STRING_SIZE);
 	gtd->out            = str_alloc(STRING_SIZE);
 
-	for (index = 0 ; index < LIST_MAX ; index++)
-	{
-		gts->list[index] = init_list(gts, index, 32);
-	}
-
-	gts->name           = strdup("gts");
-	gts->group          = strdup("");
-	gts->session_host   = strdup("");
-	gts->session_port   = strdup("");
-	gts->cmd_color      = strdup("");
-	gts->telopts        = TELOPT_FLAG_ECHO;
-	gts->flags          = SES_FLAG_MCCP;
-	gts->socket         = 1;
-	gts->read_max       = 16384;
-	gts->lognext_name   = strdup("");
-	gts->logline_name   = strdup("");
-
 	gtd->flags          = TINTIN_FLAG_INHERITANCE;
 
 	gtd->mccp_len       = 10000;
@@ -351,11 +380,14 @@ void init_tintin(int greeting)
 
 	gtd->input_off      = 1;
 
-	gtd->os             = strdup(getenv("OS") ? getenv("OS") : "UNKNOWN");
+	gtd->os             = strdup(getenv("OS")   ? getenv("OS")   : "UNKNOWN");
 	gtd->home           = strdup(getenv("HOME") ? getenv("HOME") : "~/");
 	gtd->lang           = strdup(getenv("LANG") ? getenv("LANG") : "UNKNOWN");
 	gtd->term           = strdup(getenv("TERM") ? getenv("TERM") : "UNKNOWN");
 
+	gtd->detach_file    = strdup("");
+	gtd->attach_file    = strdup("");
+
 	gtd->time           = time(NULL);
 	gtd->calendar       = *localtime(&gtd->time);
 
@@ -371,7 +403,7 @@ void init_tintin(int greeting)
 		{
 			if (index && strcmp(command_table[index - 1].name, command_table[index].name) > 0)
 			{
-				printf("\e[1;31minit_tintin() unsorted command table %s vs %s.", command_table[index - 1].name, command_table[index].name);
+				print_stdout("\e[1;31minit_tintin() unsorted command table %s vs %s.", command_table[index - 1].name, command_table[index].name);
 			}
 
 			if (*command_table[index].name == 'a' + ref)
@@ -391,24 +423,59 @@ void init_tintin(int greeting)
 
 		if (strcmp(event_table[index - 1].name, event_table[index].name) > 0)
 		{
-			printf("\e[1;31minit_tintin() unsorted event table %s vs %s.", event_table[index - 1].name, event_table[index].name);
+			print_stdout("\e[1;31minit_tintin() unsorted event table %s vs %s.", event_table[index - 1].name, event_table[index].name);
 
 			break;
 		}
 	}
-	init_terminal_size(gts);
+
+	gtd->screen = calloc(1, sizeof(struct screen_data));
+
+	gtd->screen->rows   = SCREEN_HEIGHT;
+	gtd->screen->cols   = SCREEN_WIDTH;
+	gtd->screen->height = SCREEN_HEIGHT * 16;
+	gtd->screen->width  = SCREEN_WIDTH * 10;
+	gtd->screen->focus  = 1;
 
 	init_msdp_table();
 
-	init_local(gts);
 
-	printf("\e="); // set application keypad mode
 
-	gtd->input_level++;
+	// global tintin session
+
+	gts = (struct session *) calloc(1, sizeof(struct session));
+
+	gts->name           = strdup("gts");
+	gts->group          = strdup("");
+	gts->session_host   = strdup("");
+	gts->session_port   = strdup("");
+	gts->cmd_color      = strdup("");
+	gts->telopts        = TELOPT_FLAG_ECHO;
+	gts->flags          = SES_FLAG_MCCP;
+	gts->socket         = 1;
+	gts->read_max       = 16384;
+	gts->lognext_name   = strdup("");
+	gts->logline_name   = strdup("");
+
+	gtd->ses = gts;
+
+	for (index = 0 ; index < LIST_MAX ; index++)
+	{
+		gts->list[index] = init_list(gts, index, 32);
+	}
+
+	gts->split  = calloc(1, sizeof(struct split_data));
+	gts->scroll = calloc(1, sizeof(struct scroll_data));
+
+	init_terminal_size(gts);
+
+	init_local(gts);
+
+	gtd->level->input++;
 
 	do_configure(gts, "{AUTO TAB}         {5000}");
-	do_configure(gts, "{BUFFER SIZE}     {20000}");
-	do_configure(gts, "{COLOR MODE}       {AUTO}");
+	do_configure(gts, "{BUFFER SIZE}     {10000}");
+	do_configure(gts, "{COLOR MODE}         {ON}");
 	do_configure(gts, "{COLOR PATCH}       {OFF}");
 	do_configure(gts, "{COMMAND COLOR}   {<078>}");
 	do_configure(gts, "{COMMAND ECHO}       {ON}");
@@ -421,17 +488,10 @@ void init_tintin(int greeting)
 	do_configure(gts, "{RANDOM SEED}      {AUTO}");
 	do_configure(gts, "{REPEAT CHAR}         {!}");
 	do_configure(gts, "{REPEAT ENTER}      {OFF}");
-
-	if (HAS_BIT(greeting, STARTUP_FLAG_SCREENREADER))
-	{
-		do_configure(gts, "{SCREEN READER}     {ON}");
-	}
-	else
-	{
-		do_configure(gts, "{SCREEN READER}     {OFF}");
-	}
+	do_configure(gts, HAS_BIT(greeting, STARTUP_FLAG_SCREENREADER) ? "{SCREEN READER} {ON}" : "{SCREEN READER} {OFF}");
 	do_configure(gts, "{SCROLL LOCK}        {ON}");
 	do_configure(gts, "{SPEEDWALK}         {OFF}");
+	do_configure(gts, "{TAB WIDTH}        {AUTO}");
 	do_configure(gts, "{TELNET}             {ON}");
 	do_configure(gts, "{TINTIN CHAR}         {#}");
 	do_configure(gts, "{VERBATIM}          {OFF}");
@@ -439,7 +499,7 @@ void init_tintin(int greeting)
 	do_configure(gts, "{VERBOSE}           {OFF}");
 	do_configure(gts, "{WORDWRAP}           {ON}");
 
-	gtd->input_level--;
+	gtd->level->input--;
 
 	insert_node_list(gts->list[LIST_PATHDIR],  "n",  "s",  "1", "");
 	insert_node_list(gts->list[LIST_PATHDIR],  "e",  "w",  "2", "");
@@ -455,7 +515,6 @@ void init_tintin(int greeting)
 
 	init_terminal(gts);
 
-	if (!HAS_BIT(greeting, STARTUP_FLAG_NORESET))
 	{
 		reset_screen(gts);
 	}
@@ -490,6 +549,8 @@ void init_tintin(int greeting)
 			}
 		}
 	}
+	pop_call();
+	return;
 }
 
 
@@ -500,7 +561,7 @@ void quitmsg(char *message)
 
 	if (crashed++)
 	{
-		printf("quitmsg(crashed)\n");
+		print_stdout("quitmsg(crashed)\n");
 
 		fflush(NULL);
 
@@ -530,6 +591,8 @@ void quitmsg(char *message)
 		history_write(gts, filename);
 	}
 
+	reset_daemon();
+
 	reset_terminal(gts);
 
 	reset_screen(gts);
@@ -538,9 +601,9 @@ void quitmsg(char *message)
 	{
 		if (message)
 		{
-			printf("\n\e[0m%s", message);
+			print_stdout("\n\e[0m%s", message);
 		}
-		printf("\nGoodbye from TinTin++\n\n");
+		print_stdout("\nGoodbye from TinTin++\n\n");
 	}
 	fflush(NULL);
 
@@ -596,7 +659,7 @@ void syserr_signal(int signal, char *msg)
 
 	if (crashed++)
 	{
-		printf("syserr_signal(crashed)\n");
+		print_stdout("\ecsyserr_signal(crashed)\n");
 
 		fflush(NULL);
 
@@ -607,11 +670,13 @@ void syserr_signal(int signal, char *msg)
 
 	reset_screen(gts);
 
+	fflush(NULL);
+
 	dump_stack_fatal();
 
 	sprintf(buf, "\e[1;31mFATAL SIGNAL FROM (%s): %s\e[0m\n", msg, strsignal(signal));
 
-	printf("%s", buf);
+	print_stdout("%s", buf);
 
 	fflush(NULL);
 
@@ -625,7 +690,7 @@ void syserr_fatal(int signal, char *msg)
 
 	if (crashed++)
 	{
-		printf("syserr_fatal(crashed)");
+		print_stdout("\ecsyserr_fatal(crashed)");
 
 		fflush(NULL);
 
@@ -641,20 +706,24 @@ void syserr_fatal(int signal, char *msg)
 		sprintf(errstr, "(signal %d: %s)", signal, strsignal(signal));
 	}
 
-	if (gtd->quiet_level)
+	if (gtd->level->quiet)
 	{
-		gtd->quiet_level = 0;
+		gtd->level->quiet = 0;
 	}
 
 	reset_terminal(gts);
 
 	reset_screen(gts);
 
+	print_stdout("\e[r");
+
 	dump_stack_fatal();
 
 	sprintf(buf, "\n\e[1;31mFATAL ERROR \e[1;32m%s %s\e[0m\n", msg, errstr);
 
-	printf("%s", buf);
+	print_stdout("%s", buf);
+
+	reset_daemon();
 
 	fflush(NULL);
 

Різницю між файлами не показано, бо вона завелика
+ 393 - 108
src/mapper.c


+ 188 - 61
src/math.c

@@ -28,7 +28,8 @@
 
 #define EXP_VARIABLE         0
 #define EXP_STRING           1
-#define EXP_OPERATOR         2
+#define EXP_BRACE            2
+#define EXP_OPERATOR         3
 
 #define EXP_PR_DICE          0
 #define EXP_PR_INTMUL        1
@@ -95,6 +96,38 @@ long double get_number(struct session *ses, char *str)
 	return val;
 }
 
+unsigned long long get_integer(struct session *ses, char *str)
+{
+	unsigned long long val;
+	char result[BUFFER_SIZE];
+
+	mathexp(ses, str, result, 0);
+
+	val = tintou(result);
+
+	return val;
+}
+
+int get_ellipsis(struct listroot *root, char *name, int *min, int *max)
+{
+	unsigned long long range = get_integer(root->ses, name);
+
+	push_call("get_ellipsis(%p,%p,%p,%p)",root,name,min,max);
+
+
+	*min = (int) (HAS_BIT(range, 0x00000000FFFFFFFFLL));
+	*max = (int) (HAS_BIT(range, 0xFFFFFFFF00000000ULL) >> 32ULL);
+
+	*min = *min > 0 ? *min - 1 : root->used + *min;
+	*max = *max > 0 ? *max - 1 : root->used + *max;
+
+	*min = URANGE(0, *min, root->used - 1);
+	*max = URANGE(0, *max, root->used - 1);
+
+	pop_call();
+	return 1 + (*min < *max ? *max - *min : *min - *max);
+}
+
 long double get_double(struct session *ses, char *str)
 {
 	long double val;
@@ -277,7 +310,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 							return FALSE;
 						}
 						*pta++ = *pti++;
-						status = EXP_STRING;
+						status = EXP_BRACE;
 						nest++;
 						break;
 					case '"':
@@ -317,17 +350,24 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 						break;
 						
 					case '.':
-						*pta++ = *pti++;
-						if (point >= 0)
+						if (pta != buf3 && pti[1] == '.')
 						{
-							if (debug)
+							MATH_NODE(FALSE, EXP_PR_VAR, EXP_OPERATOR);
+						}
+						else
+						{
+							*pta++ = *pti++;
+							if (point >= 0)
 							{
-								show_debug(ses, LIST_VARIABLE, "#MATH EXP: MORE THAN ONE POINT FOUND INSIDE A NUMBER");
+								if (debug)
+								{
+									show_debug(ses, LIST_VARIABLE, "#MATH EXP: MORE THAN ONE POINT FOUND INSIDE A NUMBER");
+								}
+								precision = 0;
+								return FALSE;
 							}
-							precision = 0;
-							return FALSE;
+							point++;
 						}
-						point++;
 						break;
 
 					case ' ':
@@ -373,12 +413,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 			case EXP_STRING:
 				switch (*pti)
 				{
-					case '{':
-						*pta++ = *pti++;
-						nest++;
-						break;
-
-					case '}':
+					case '"':
 						*pta++ = *pti++;
 						nest--;
 						if (nest == 0)
@@ -387,7 +422,21 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 						}
 						break;
 
-					case '"':
+					default:
+						*pta++ = *pti++;
+						break;
+				}
+				break;
+
+			case EXP_BRACE:
+				switch (*pti)
+				{
+					case '{':
+						*pta++ = *pti++;
+						nest++;
+						break;
+
+					case '}':
 						*pta++ = *pti++;
 						nest--;
 						if (nest == 0)
@@ -409,6 +458,25 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 						pti++;
 						break;
 
+					case '.':
+						if (pti[1] == '.')
+						{
+							*pta++ = *pti++;
+							*pta++ = *pti++;
+
+							MATH_NODE(FALSE, EXP_PR_LOGCOMP, EXP_VARIABLE);
+							break;
+						}
+						else
+						{
+							if (debug)
+							{
+								show_debug(ses, LIST_VARIABLE, "#MATH EXP: UNKNOWN OPERATOR: %c%c", pti[0], pti[1]);
+							}
+							return FALSE;
+						}
+						break;
+
 					case ')':
 						*pta++ = *pti++;
 						level--;
@@ -560,6 +628,10 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 						{
 							case '=':
 								*pta++ = *pti++;
+								if (*pti == '=')
+								{
+									*pta++ = *pti++;
+								}
 								MATH_NODE(FALSE, EXP_PR_LOGCOMP, EXP_VARIABLE);
 								break;
 
@@ -656,10 +728,21 @@ void mathexp_level(struct session *ses, struct link_data *node)
 void mathexp_compute(struct session *ses, struct link_data *node)
 {
 	char temp[BUFFER_SIZE];
-	long double value;
+	int integer64 = 0;
+	long double value = 0;
+	unsigned long long min = 0, max = 0;
+	unsigned long long value64 = 0;
 
 	switch (node->str3[0])
 	{
+		case '.':
+			integer64 = 1;
+
+                        SET_BIT(max, (unsigned int) tintoi(node->next->str3));
+                        max = max << 32ULL;
+                        SET_BIT(min, (unsigned int) tintoi(node->prev->str3));
+                        value64 = max | min;
+
 		case 'd':
 			if (tintoi(node->next->str3) <= 0)
 			{
@@ -723,9 +806,9 @@ void mathexp_compute(struct session *ses, struct link_data *node)
 			}
 			else
 			{
-				// Can't perform modulo on doubles
+				integer64 = 1;
 
-				value = (long long) tintoi(node->prev->str3) % (long long) tintoi(node->next->str3);
+				value64 = tintou(node->prev->str3) % tintou(node->next->str3);
 			}
 			break;
 		case '+':
@@ -741,7 +824,7 @@ void mathexp_compute(struct session *ses, struct link_data *node)
 					value = tincmp(node->prev->str3, node->next->str3) <= 0;
 					break;
 				case '<':
-					value = (long long) tintoi(node->prev->str3) << (long long) tintoi(node->next->str3);
+					value = atoll(node->prev->str3) << atoll(node->next->str3);
 					break;
 				default:
 					value = tincmp(node->prev->str3, node->next->str3) < 0;
@@ -755,7 +838,7 @@ void mathexp_compute(struct session *ses, struct link_data *node)
 					value = tincmp(node->prev->str3, node->next->str3) >= 0;
 					break;
 				case '>':
-					value = (long long) tintoi(node->prev->str3) >> (long long) tintoi(node->next->str3);
+					value = atoll(node->prev->str3) >> atoll(node->next->str3);
 					break;
 				default:
 					value = tincmp(node->prev->str3, node->next->str3) > 0;
@@ -770,7 +853,7 @@ void mathexp_compute(struct session *ses, struct link_data *node)
 					value = tintoi(node->prev->str3) && tintoi(node->next->str3);
 					break;
 				default:
-					value = (long long) tintoi(node->prev->str3) & (long long) tintoi(node->next->str3);
+					value = atoll(node->prev->str3) & atoll(node->next->str3);
 					break;
 			}
 			break;
@@ -782,7 +865,7 @@ void mathexp_compute(struct session *ses, struct link_data *node)
 					break;
 
 				default:
-					value = (long long) tintoi(node->prev->str3) ^ (long long) tintoi(node->next->str3);
+					value = atoll(node->prev->str3) ^ atoll(node->next->str3);
 					break;
 			}
 			break;
@@ -794,16 +877,32 @@ void mathexp_compute(struct session *ses, struct link_data *node)
 					break;
 
 				default:
-					value = (long long) tintoi(node->prev->str3) | (long long) tintoi(node->next->str3);
+					value = atoll(node->prev->str3) | atoll(node->next->str3);
 					break;
 			}
 			break;
 		case '=':
-			value = (tineval(ses, node->prev->str3, node->next->str3) != 0);
+			if (node->str3[1] == '=' && node->str3[2] == '=')
+			{
+				
+				value = tincmp(node->prev->str3, node->next->str3) == 0;
+			}
+			else
+			{
+				value = tineval(ses, node->prev->str3, node->next->str3) != 0;
+			}
 			break;
 		case '!':
-			value = (tineval(ses, node->prev->str3, node->next->str3) == 0);
+			if (node->str3[1] == '=' && node->str3[2] == '=')
+			{
+				value = tincmp(node->prev->str3, node->next->str3) != 0;
+			}
+			else
+			{
+				value = tineval(ses, node->prev->str3, node->next->str3) == 0;
+			}
 			break;
+
 		default:
 			show_debug(ses, LIST_VARIABLE, "#COMPUTE EXP: UNKNOWN OPERATOR: %c%c", node->str3[0], node->str3[1]);
 			value = 0;
@@ -827,7 +926,14 @@ void mathexp_compute(struct session *ses, struct link_data *node)
 	free(node->str2);
 	node->str2 = strdup(temp);
 
-	sprintf(temp, "%.*Lf", precision, value);
+	if (integer64)
+	{
+		sprintf(temp, "%llu", value64);
+	}
+	else
+	{
+		sprintf(temp, "%.*Lf", precision, value);
+	}
 	free(node->str3);
 	node->str3 = strdup(temp);
 }
@@ -952,66 +1058,89 @@ long double tintoi(char *str)
 		default:
 			return (values[0] + values[1] + values[2] + values[3] + values[4]);
 	}
+}
 
-	switch (str[0])
+unsigned long long tintou(char *str)
+{
+	char *ptr = str;
+	unsigned long long value = 0, m = 1;
+
+	if (*ptr == 0)
 	{
-		case '!':
-			return !atof(&str[1]);
+		return 0;
+	}
 
+	switch (*ptr)
+	{
+		case '!':
 		case '~':
-			return (long double) ~atoll(&str[1]);
-
 		case '+':
-			return +atof(&str[1]);
-
 		case '-':
-			return -atof(&str[1]);
-
-		default:
-			return atof(str);
+			ptr++;
+			break;
 	}
 
-	while (*ptr)
+	ptr = str + strlen(str);
+
+	while (ptr != str)
 	{
-		if (!isdigit((int) *ptr))
+		ptr--;
+
+		switch (*ptr)
 		{
-			if (*ptr != '.')
-			{
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				value += (*ptr - '0') * m;
+				m *= 10;
+				break;
+
+			case '.':
+				*ptr = 0;
+				break;
+
+			case ':':
 				return 0;
-			}
-			ptr++;
+				break;
 
-			while (*ptr)
-			{
-				if (!isdigit((int) *ptr))
+			case '!':
+			case '~':
+			case '+':
+			case '-':
+				if (ptr == str)
 				{
-					return 0;
+					break;
 				}
-				ptr++;
-			}
-		}
-		else
-		{
-			ptr++;
+				return 0;
+
+			default:
+				return 0;
 		}
 	}
 
 	switch (str[0])
 	{
 		case '!':
-			return !atof(&str[1]);
+			return !value;
 
 		case '~':
-			return (long double) ~atoll(&str[1]);
+			return ~value;
 
 		case '+':
-			return +atof(&str[1]);
+			return +value;
 
 		case '-':
-			return -atof(&str[1]);
+			return -value;
 
 		default:
-			return atof(str);
+			return value;
 	}
 }
 
@@ -1097,8 +1226,6 @@ long double tincmp(char *left, char *right)
 	switch (left[0])
 	{
 		case '{':
-			return strcmp(left, right);
-
 		case '"':
 			return strcmp(left, right);
 

+ 82 - 32
src/misc.c

@@ -29,8 +29,82 @@
 
 DO_COMMAND(do_bell)
 {
-	printf("\007");
+	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
 
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0)
+	{
+		print_stdout("\007");
+
+		return ses;
+	}
+
+	if (is_abbrev(arg1, "FLASH"))
+	{
+		if (is_abbrev(arg2, "ON"))
+		{
+			print_stdout("\e[?1042h");
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			print_stdout("\e[?1042l");
+		}
+		else
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #BELL FLASH {ON|OFF}");
+		}
+	}
+	else if (is_abbrev(arg1, "FOCUS"))
+	{
+		if (is_abbrev(arg2, "ON"))
+		{
+			print_stdout("\e[?1043h");
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			print_stdout("\e[?1043l");
+		}
+		else
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #BELL POP {ON|OFF}");
+		}
+	}
+	else if (is_abbrev(arg1, "MARGIN"))
+	{
+		if (is_abbrev(arg2, "ON"))
+		{
+			print_stdout("\e[?44h");
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			print_stdout("\e[?44l");
+		}
+		else
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #BELL MARGIN {ON|OFF}");
+		}
+	}
+	else if (is_abbrev(arg1, "RING"))
+	{
+		print_stdout("\007");
+	}
+	else if (is_abbrev(arg1, "VOLUME"))
+	{
+		if (is_math(ses, arg2))
+		{
+			print_stdout("\e[ %dt", (int) get_number(ses, arg2));
+		}
+		else
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #BELL VOLUME {1-8}");
+		}
+	}
+	else
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #BELL {FLASH|FOCUS|MARGIN|RING|VOLUME} {ARGUMENT}");
+	}
 	return ses;
 }
 
@@ -122,15 +196,17 @@ DO_COMMAND(do_echo)
 		if (!HAS_BIT(ses->flags, SES_FLAG_READMUD) && IS_SPLIT(ses))
 		{
 			save_pos(ses);
-			goto_rowcol(ses, ses->bot_row, 1);
-		}
 
-		print_line(ses, &output, lnf);
+			goto_pos(ses, ses->split->bot_row, 1);
 
-		if (!HAS_BIT(ses->flags, SES_FLAG_READMUD) && IS_SPLIT(ses))
-		{
+			print_line(ses, &output, lnf);
+			
 			restore_pos(ses);
 		}
+		else
+		{
+			print_line(ses, &output, lnf);
+		}
 	}
 	str_free(output);
 
@@ -156,14 +232,6 @@ DO_COMMAND(do_end)
 }
 
 
-DO_COMMAND(do_forall)
-{
-	tintin_printf2(ses, "\e[1;31m#NOTICE: The #forall command no longer exists, please switch to the #foreach command.\n");
-
-	return ses;
-}
-
-
 DO_COMMAND(do_nop)
 {
 	return ses;
@@ -189,23 +257,5 @@ DO_COMMAND(do_send)
 
 DO_COMMAND(do_test)
 {
-/*
-	char pts;
-	unsigned char ptu;
-	int bla = 0;
-
-	if (*arg == 'c')
-	{
-		strcpy((char *) bla, "crash baby crash");
-	}
-
-	pts = 140;
-	ptu = 140;
-
-	printf("pts: %d %d\n", pts, (unsigned char) pts);
-	printf("ptu: %d %d\n", (signed char) ptu, ptu);
-	printf("pts: %d %d\n", pts, (unsigned int) pts);
-	printf("ptu: %d %d\n", (signed int) ptu, ptu);
-*/
 	return ses;
 }

+ 1 - 1
src/msdp.c

@@ -39,7 +39,7 @@ void init_msdp_table(void)
 		{
 			if (*msdp_table[index+1].name)
 			{
-				tintin_printf2(NULL, "\e[31minit_msdp_table: Improperly sorted variable: %s.\e0m", msdp_table[index]);
+				print_stdout("\e[31minit_msdp_table: Improperly sorted variable: %s.\e0m", msdp_table[index+1].name);
 			}
 		}
 	}

+ 184 - 2
src/nest.c

@@ -184,12 +184,92 @@ int delete_nest_node(struct listroot *root, char *variable)
 // Return the number of indices of a node.
 
 int get_nest_size(struct listroot *root, char *variable)
+{
+	char name[BUFFER_SIZE], *arg;
+	int index, count;
+	arg = get_arg_to_brackets(root->ses, variable, name);
+
+	if (!strcmp(arg, "[]"))
+	{
+		if (*name == 0)
+		{
+			return root->used + 1;
+		}
+
+		if (search_nest_root(root, name) == NULL)
+		{
+			if (search_node_list(root, name))
+			{
+				return 1;
+			}
+		}
+	}
+
+	while (root && *name)
+	{
+		// Handle regex queries
+
+		if (search_nest_root(root, name) == NULL)
+		{
+			if (search_node_list(root, name) == NULL)
+			{
+				if (tintin_regexp_check(root->ses, name))
+				{
+					for (index = count = 0 ; index < root->used ; index++)
+					{
+						if (match(root->ses, root->list[index]->arg1, name, SUB_NONE))
+						{
+							count++;
+						}
+					}
+					return count + 1;
+				}
+				else if (strstr(name, "..") && is_math(root->ses, name))
+				{
+					int min, max, range;
+
+					if (root->used)
+					{
+						range = get_ellipsis(root, name, &min, &max);
+
+						return range + 1;
+					}
+					else
+					{
+						return 1;
+					}
+				}
+				else
+				{
+					return 0;
+				}
+			}
+		}
+
+		root = search_nest_root(root, name);
+
+		if (root)
+		{
+			if (!strcmp(arg, "[]"))
+			{
+				return root->used + 1;
+			}
+			arg = get_arg_in_brackets(root->ses, arg, name);
+		}
+	}
+
+	return 0;
+}
+
+int get_nest_size_index(struct listroot *root, char *variable, char **result)
 {
 	char name[BUFFER_SIZE], *arg;
 	int index, count;
 
 	arg = get_arg_to_brackets(root->ses, variable, name);
 
+	str_cpy(result, "");
+
 	if (!strcmp(arg, "[]"))
 	{
 		if (*name == 0)
@@ -225,6 +305,21 @@ int get_nest_size(struct listroot *root, char *variable)
 					}
 					return count + 1;
 				}
+				else if (strstr(name, "..") && is_math(root->ses, name))
+				{
+					int min, max, range;
+
+					if (root->used)
+					{
+						range = get_ellipsis(root, name, &min, &max);
+
+						return range + 1;
+					}
+					else
+					{
+						return 1;
+					}
+				}
 				else
 				{
 					return 0;
@@ -296,6 +391,35 @@ int get_nest_size_key(struct listroot *root, char *variable, char **result)
 					}
 					return count + 1;
 				}
+				else if (strstr(name, "..") && is_math(root->ses, name))
+				{
+					int min, max, range;
+
+					if (root->used)
+					{
+						range = get_ellipsis(root, name, &min, &max);
+
+						if (min < max)
+						{
+							while (min <= max)
+							{
+								str_cat_printf(result, "{%s}", root->list[min++]->arg1);
+							}
+						}
+						else
+						{
+							while (min >= max)
+							{
+								str_cat_printf(result, "{%s}", root->list[min--]->arg1);
+							}
+						}
+						return range + 1;
+					}
+					else
+					{
+						return 1;
+					}
+				}
 				else
 				{
 					return 0;
@@ -326,6 +450,7 @@ int get_nest_size_val(struct listroot *root, char *variable, char **result)
 {
 	char name[BUFFER_SIZE], *arg;
 	int index, count;
+	static int warning;
 
 	arg = get_arg_to_brackets(root->ses, variable, name);
 
@@ -371,6 +496,35 @@ int get_nest_size_val(struct listroot *root, char *variable, char **result)
 					}
 					return count + 1;
 				}
+				else if (strstr(name, "..") && is_math(root->ses, name))
+				{
+					int min, max, range;
+
+					if (root->used)
+					{
+						range = get_ellipsis(root, name, &min, &max);
+
+						if (min < max)
+						{
+							while (min <= max)
+							{
+								show_nest_node(root->list[min++], result, FALSE);
+							}
+						}
+						else
+						{
+							while (min >= max)
+							{
+								show_nest_node(root->list[min--], result, FALSE);
+							}
+						}
+						return range + 1;
+					}
+					else
+					{
+						return 1;
+					}
+				}
 				else
 				{
 					return 0;
@@ -384,6 +538,11 @@ int get_nest_size_val(struct listroot *root, char *variable, char **result)
 		{
 			if (!strcmp(arg, "[]"))
 			{
+				if (++warning < 100)
+				{
+					tintin_printf2(root->ses, "\n\e[1;5;31mdebug: please use *%s instead of $%s.\n", variable, variable);
+				}
+
 				for (index = 0 ; index < root->used ; index++)
 				{
 					str_cat_printf(result, "{%s}", root->list[index]->arg1);
@@ -416,6 +575,10 @@ struct listnode *get_nest_node_key(struct listroot *root, char *variable, char *
 	{
 		str_cpy_printf(result, "%s", node->arg1);
 
+		if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+		{
+			delete_nest_node(root, variable);
+		}
 		return node;
 	}
 
@@ -432,6 +595,8 @@ struct listnode *get_nest_node_key(struct listroot *root, char *variable, char *
 	return NULL;
 }
 
+// Has ONESHOT check
+
 struct listnode *get_nest_node_val(struct listroot *root, char *variable, char **result, int def)
 {
 	struct listnode *node;
@@ -450,6 +615,10 @@ struct listnode *get_nest_node_val(struct listroot *root, char *variable, char *
 	{
 		show_nest_node(node, result, TRUE);
 
+		if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+		{
+			delete_nest_node(root, variable);
+		}
 		return node;
 	}
 
@@ -472,7 +641,7 @@ int get_nest_index(struct listroot *root, char *variable, char **result, int def
 	struct listnode *node;
 	int index, size;
 
-	size = get_nest_size_val(root, variable, result);
+	size = get_nest_size_index(root, variable, result);
 
 	if (size)
 	{
@@ -481,12 +650,17 @@ int get_nest_index(struct listroot *root, char *variable, char **result, int def
 		return -1;
 	}
 
+	node = search_nest_node(root, variable);
 	index = search_nest_index(root, variable);
 
-	if (index >= 0)
+	if (node && index >= 0)
 	{
 		str_cpy_printf(result, "%d", index + 1);
 
+		if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+		{
+			delete_index_list(root, index);
+		}
 		return index;
 	}
 
@@ -643,6 +817,14 @@ struct listnode *set_nest_node(struct listroot *root, char *arg1, char *format,
 	{
 		node = update_node_list(root, name, arg2, "", "");
 	}
+
+	if (gtd->level->oneshot)
+	{
+		SET_BIT(node->flags, NODE_FLAG_ONESHOT);
+	}
+
+	check_all_events(root->ses, SUB_ARG, 1, 1, "VARIABLE UPDATED %s", name, name, arg2);
+
 	free(arg2);
 
 	pop_call();

+ 27 - 25
src/net.c

@@ -27,16 +27,15 @@
 
 #include "tintin.h"
 
-#include <sys/types.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
 #include <netinet/in.h>
 #include <netdb.h>
 #include <signal.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <fcntl.h>
+#include <sys/types.h>
 
 /*
-	IPv6 compatible connect code, doesn't work on several platforms.
+	IPv6 compatible connect code.
 */
 
 #ifdef HAVE_GETADDRINFO
@@ -86,18 +85,20 @@ int connect_mud(struct session *ses, char *host, char *port)
 		return -1;
 	}
 
-	ses->connect_error = connect(sock, address->ai_addr, address->ai_addrlen);
+//	fcntl(sock, F_SETFL, O_NDELAY);
 
-	if (ses->connect_error)
-	{
-		syserr_printf(ses, "connect_mud: connect");
+        ses->connect_error = connect(sock, address->ai_addr, address->ai_addrlen);
 
-		close(sock);
+        if (ses->connect_error)
+        {
+                syserr_printf(ses, "connect_mud: connect");
 
-		freeaddrinfo(address);
+                close(sock);
 
-		return -1;
-	}
+                freeaddrinfo(address);
+
+                return -1;
+        }
 
 	if (fcntl(sock, F_SETFL, O_NDELAY|O_NONBLOCK) == -1)
 	{
@@ -133,7 +134,7 @@ int connect_mud(struct session *ses, char *host, char *port)
 	int sock, d;
 	struct sockaddr_in sockaddr;
 
-	printf("debug: NO ADDRESS INFO?\n");
+	print_stdout("debug: NO ADDRESS INFO?\n");
 
 	if (sscanf(host, "%d.%d.%d.%d", &d, &d, &d, &d) == 4)
 	{
@@ -224,7 +225,7 @@ void write_line_mud(struct session *ses, char *line, int size)
 
 	if (!HAS_BIT(ses->telopts, TELOPT_FLAG_TELNET))
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5TOUTF8))
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5TOUTF8))
 		{
 			char buf[BUFFER_SIZE];
 
@@ -232,7 +233,7 @@ void write_line_mud(struct session *ses, char *line, int size)
 
 			strcpy(line, buf);
 		}
-		else if (HAS_BIT(ses->flags, SES_FLAG_KOI8TOUTF8))
+		else if (HAS_BIT(ses->charset, CHARSET_FLAG_KOI8TOUTF8))
 		{
 			char buf[BUFFER_SIZE];
 
@@ -350,7 +351,8 @@ void readmud(struct session *ses)
 	if (HAS_BIT(gtd->ses->flags, SES_FLAG_SPLIT))
 	{
 		save_pos(gtd->ses);
-		goto_rowcol(gtd->ses, gtd->ses->bot_row, 1);
+
+		goto_pos(gtd->ses, gtd->ses->split->bot_row, 1);
 	}
 
 	SET_BIT(cts->flags, SES_FLAG_READMUD);
@@ -387,10 +389,10 @@ void readmud(struct session *ses)
 			{
 				if (!HAS_BIT(ses->telopts, TELOPT_FLAG_PROMPT))
 				{
-					if (gts->check_output)
+					if (ses->packet_patch)
 					{
 						strcat(ses->more_output, line);
-						ses->check_output = utime() + gts->check_output;
+						ses->check_output = utime() + ses->packet_patch;
 
 						break;
 					}
@@ -424,15 +426,15 @@ void readmud(struct session *ses)
 			strcpy(linebuf, line);
 		}
 
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5TOUTF8) || HAS_BIT(ses->flags, SES_FLAG_FANSITOUTF8) || HAS_BIT(ses->flags, SES_FLAG_KOI8TOUTF8))
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5TOUTF8) || HAS_BIT(ses->charset, CHARSET_FLAG_FANSITOUTF8) || HAS_BIT(ses->charset, CHARSET_FLAG_KOI8TOUTF8))
 		{
 			char tempbuf[BUFFER_SIZE];
 
-			if (HAS_BIT(ses->flags, SES_FLAG_BIG5TOUTF8))
+			if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5TOUTF8))
 			{
 				big5_to_utf8(linebuf, tempbuf);
 			}
-			else if (HAS_BIT(ses->flags, SES_FLAG_FANSITOUTF8))
+			else if (HAS_BIT(ses->charset, CHARSET_FLAG_FANSITOUTF8))
 			{
 				fansi_to_utf8(linebuf, tempbuf);
 			}
@@ -490,9 +492,9 @@ void process_mud_output(struct session *ses, char *linebuf, int prompt)
 
 	if (HAS_BIT(ses->flags, SES_FLAG_COLORPATCH))
 	{
-		sprintf(line, "%s%s%s", ses->color, linebuf, "\e[0m");
+		sprintf(line, "%s%s%s", ses->color_patch, linebuf, "\e[0m");
 
-		get_color_codes(ses->color, linebuf, ses->color);
+		get_color_codes(ses->color_patch, linebuf, ses->color_patch, GET_ALL);
 
 		linebuf = line;
 	}
@@ -509,7 +511,7 @@ void process_mud_output(struct session *ses, char *linebuf, int prompt)
 
 		strip_non_vt102_codes(linebuf, line);
 
-		printf("%s", line);
+		print_stdout("%s", line);
 
 		strip_vt102_codes(linebuf, line);
 

+ 14 - 9
src/parse.c

@@ -53,6 +53,8 @@ int case_table[256] =
 
 int is_abbrev(char *str1, char *str2)
 {
+	char *str3 = gtd->is_result;
+
 	if (*str1 == 0)
 	{
 		return false;
@@ -61,6 +63,7 @@ int is_abbrev(char *str1, char *str2)
 	if (*str2 == 0)
 	{
 		tintin_printf2(gtd->ses, "\e[1;31mis_abbrev(%s,%s)", str1, str2);
+
 		dump_stack();
 
 		return false;
@@ -70,6 +73,8 @@ int is_abbrev(char *str1, char *str2)
 	{
 		if (*str1 == 0)
 		{
+			strcpy(str3, str2);
+
 			return true;
 		}
 
@@ -78,7 +83,7 @@ int is_abbrev(char *str1, char *str2)
 			return false;
 		}
 		str1++;
-		str2++;
+		*str3++ = *str2++;
 	}
 }
 
@@ -367,7 +372,7 @@ char *get_arg_all(struct session *ses, char *string, char *result, int verbatim)
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -437,7 +442,7 @@ char *get_arg_in_braces(struct session *ses, char *string, char *result, int fla
 	while (*pti)
 	{
 		
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -500,7 +505,7 @@ char *get_arg_with_spaces(struct session *ses, char *string, char *result, int f
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -544,7 +549,7 @@ char *get_arg_stop_spaces(struct session *ses, char *string, char *result, int f
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -615,7 +620,7 @@ char *get_arg_to_brackets(struct session *ses, char *string, char *result)
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -682,7 +687,7 @@ char *get_arg_at_brackets(struct session *ses, char *string, char *result)
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -739,7 +744,7 @@ char *get_arg_in_brackets(struct session *ses, char *string, char *result)
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -816,7 +821,7 @@ void do_one_line(char *line, struct session *ses)
 
 	push_call("[%s] do_one_line(%s)",ses->name,line);
 
-	if (gtd->ignore_level)
+	if (gtd->level->ignore)
 	{
 		pop_call();
 		return;

+ 29 - 28
src/path.c

@@ -37,7 +37,7 @@ DO_COMMAND(do_path)
 
 	if (*arg1 == 0)
 	{
-		tintin_header(ses, " PATH COMMANDS ");
+		tintin_header(ses, " PATH OPTIONS ");
 
 		for (cnt = 0 ; *path_table[cnt].fun != NULL ; cnt++)
 		{
@@ -117,6 +117,9 @@ DO_PATH(path_start)
 
 DO_PATH(path_stop)
 {
+	int index;
+	struct listroot *root = ses->list[LIST_PATH];
+
 	if (HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
 	{
 		show_message(ses, LIST_PATH, "#PATH STOP: YOU STOP MAPPING A PATH.");
@@ -125,7 +128,18 @@ DO_PATH(path_stop)
 	}
 	else
 	{
-		show_message(ses, LIST_PATH, "#PATH STOP: ERROR: YOU ARE NOT MAPPING A PATH.");
+		if (root->list[root->update]->data)
+		{
+			for (index = 0 ; index < root->used ; index++)
+			{
+				root->list[index]->data = 0;
+			}
+			show_message(ses, LIST_PATH, "#PATH STOP: YOU STOP RUNNING A PATH.");
+		}
+		else
+		{
+			show_message(ses, LIST_PATH, "#PATH STOP: YOU ARE NOT MAPPING OR RUNNING A PATH.");
+		}
 	}
 }
 
@@ -216,12 +230,6 @@ DO_PATH(path_map)
 
 		for (i = 0 ; i < root->update ; i++)
 		{
-			if ((int) strlen(buf) + (int) strlen(root->list[i]->arg1) > gtd->screen->cols - 4)
-			{
-				tintin_puts2(ses, buf);
-
-				sprintf(buf, "%-7s", "");
-			}
 			cat_sprintf(buf, " %s", root->list[i]->arg1);
 		}
 
@@ -231,12 +239,6 @@ DO_PATH(path_map)
 
 			for (i = root->update + 1 ; i < root->used ; i++)
 			{
-				if ((int) strlen(buf) + (int) strlen(root->list[i]->arg1) > gtd->screen->cols - 4)
-				{
-					tintin_puts2(ses, buf);
-
-					sprintf(buf, "%-7s", "");
-				}
 				cat_sprintf(buf, " %s", root->list[i]->arg1);
 			}
 		}
@@ -246,10 +248,7 @@ DO_PATH(path_map)
 			cat_sprintf(buf, " [ ]");
 		}
 
-		if (strlen(buf) > 7)
-		{
-			tintin_puts2(ses, buf);
-		}
+		tintin_puts2(ses, buf);
 	}
 }
 
@@ -420,8 +419,10 @@ DO_PATH(path_insert)
 
 DO_PATH(path_run)
 {
+	int index;
 	struct listroot *root = ses->list[LIST_PATH];
-	char arg1[BUFFER_SIZE], time[BUFFER_SIZE], name[BUFFER_SIZE];
+	char arg1[BUFFER_SIZE];
+	long long total, delay;
 
 	push_call("path_run(%p,%p)",ses,arg);
 
@@ -433,20 +434,19 @@ DO_PATH(path_run)
 	}
 	else
 	{
-		DEL_BIT(ses->flags, SES_FLAG_PATHMAPPING);
-
 		if (*arg1)
 		{
-			double delay = 0;
+		
+			delay = (long long) get_number(ses, arg1) * 1000000LL;
+			total = 0;
 
-			while (root->update < root->used)
-			{
-				sprintf(name, "PATH %lld", utime());
-				sprintf(time, "%f", delay);
+			DEL_BIT(ses->flags, SES_FLAG_PATHMAPPING);
 
-				delay += get_number(ses, arg1);
+			for (index = root->update ; index < root->used ; index++)
+			{
+				root->list[index]->data = gtd->utime + total;
 
-				update_node_list(ses->list[LIST_DELAY], name, root->list[root->update++]->arg1, time, "");
+				total += delay;
 			}
 		}
 		else
@@ -457,6 +457,7 @@ DO_PATH(path_run)
 			}
 		}
 	}
+
 	pop_call();
 	return;
 }

+ 6 - 7
src/port.c

@@ -28,7 +28,6 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <sys/socket.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -45,7 +44,7 @@ DO_COMMAND(do_port)
 
 	if (*cmd == 0)
 	{
-		tintin_header(ses, " PORT COMMANDS ");
+		tintin_header(ses, " PORT OPTIONS ");
 
 		for (cnt = 0 ; *port_table[cnt].name != 0 ; cnt++)
 		{
@@ -162,7 +161,7 @@ DO_PORT(port_initialize)
 		return ses;
 	}
 
-	if (listen(sock, 50) == -1)
+	if (listen(sock, 32) == -1)
 	{
 		syserr_printf(ses, "port_initialize: listen");
 
@@ -214,16 +213,16 @@ int port_new(struct session *ses, int sock)
 {
 	struct port_data *new_buddy;
 	struct sockaddr_in sock_addr;
-	socklen_t i;
+	socklen_t len;
 	int fd;
 
 	push_call("port_new(%p,%d)",ses,sock);
 
-	i = sizeof(sock);
+	len = sizeof(sock);
 
-	getsockname(sock, (struct sockaddr *) &sock_addr, &i);
+	getsockname(sock, (struct sockaddr *) &sock_addr, &len);
 
-	if ((fd = accept(sock, (struct sockaddr *) &sock_addr, &i)) < 0)
+	if ((fd = accept(sock, (struct sockaddr *) &sock_addr, &len)) < 0)
 	{
 		syserr_printf(ses, "port_new: accept");
 

+ 11 - 13
src/regex.c

@@ -73,7 +73,7 @@ DO_COMMAND(do_regexp)
 	}
 	else
 	{
-		if (tintin_regexp(ses, NULL, arg1, arg2, 0, SUB_CMD))
+		if (tintin_regexp(ses, NULL, arg1, arg2, 0, REGEX_FLAG_CMD))
 		{
 			substitute(ses, is_t, is_t, SUB_CMD);
 
@@ -118,18 +118,18 @@ int regexp_compare(pcre *nodepcre, char *str, char *exp, int option, int flag)
 		return FALSE;
 	}
 
-	// SUB_FIX handles %1 to %99 usage. Backward compatibility.
+	// REGEX_FLAG_FIX handles %1 to %99 usage. Backward compatibility.
 
 	switch (flag)
 	{
-		case SUB_CMD:
+		case REGEX_FLAG_CMD:
 			for (i = 0 ; i < matches ; i++)
 			{
 				gtd->cmds[i] = restringf(gtd->cmds[i], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 			}
 			break;
 
-		case SUB_CMD + SUB_FIX:
+		case REGEX_FLAG_CMD + REGEX_FLAG_FIX:
 			for (i = 0 ; i < matches ; i++)
 			{
 				j = gtd->args[i];
@@ -138,14 +138,14 @@ int regexp_compare(pcre *nodepcre, char *str, char *exp, int option, int flag)
 			}
 			break;
 
-		case SUB_ARG:
+		case REGEX_FLAG_ARG:
 			for (i = 0 ; i < matches ; i++)
 			{
 				gtd->vars[i] = restringf(gtd->vars[i], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 			}
 			break;
 
-		case SUB_ARG + SUB_FIX:
+		case REGEX_FLAG_ARG + REGEX_FLAG_FIX:
 			for (i = 0 ; i < matches ; i++)
 			{
 				j = gtd->args[i];
@@ -206,7 +206,7 @@ int check_one_regexp(struct session *ses, struct listnode *node, char *line, cha
 		str = line;
 	}	
 
-	return tintin_regexp(ses, node->regex, str, exp, option, SUB_ARG);
+	return tintin_regexp(ses, node->regex, str, exp, option, REGEX_FLAG_ARG);
 }
 
 /*
@@ -223,7 +223,7 @@ int tintin_regexp_check(struct session *ses, char *exp)
 	while (*exp)
 	{
 
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *exp & 128 && exp[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *exp & 128 && exp[1] != 0)
 		{
 			exp += 2;
 			continue;
@@ -310,7 +310,7 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 
@@ -395,7 +395,7 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 					case '7':
 					case '8':
 					case '9':
-						fix = SUB_FIX;
+						fix = REGEX_FLAG_FIX;
 						arg = isdigit((int) pti[2]) ? (pti[1] - '0') * 10 + (pti[2] - '0') : pti[1] - '0';
 						gtd->args[next_arg(var)] = next_arg(arg);
 						pti += isdigit((int) pti[2]) ? 3 : 2;
@@ -599,7 +599,7 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 
@@ -891,8 +891,6 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 	}
 	*pto = 0;
 
-//	printf("debug regex compile (%s)\n", out);
-
 	return regexp_compile(out, option);
 }
 

+ 44 - 5
src/scan.c

@@ -52,7 +52,7 @@ char *get_arg_in_quotes(struct session *ses, char *string, char *result, int fla
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -100,6 +100,42 @@ char *get_arg_in_quotes(struct session *ses, char *string, char *result, int fla
 	return pti;
 }
 
+struct session *scan_bulk_file(struct session *ses, FILE *fp, char *filename, char *arg)
+{
+	char line[STRING_SIZE], *str_out, *str_rip, *str_sub;
+	int cnt = 0;
+
+	str_out = str_dup("");
+
+	while (fgets(line, BUFFER_SIZE - 1, fp))
+	{
+		cnt++;
+		str_cat(&str_out, line);
+	}
+
+	str_rip = str_alloc(str_len(str_out));
+
+	strip_vt102_codes(str_out, str_rip);
+
+	RESTRING(gtd->cmds[0], str_out);
+	RESTRING(gtd->cmds[1], str_rip);
+	RESTRING(gtd->cmds[2], ntos(str_len(str_out)));
+	RESTRING(gtd->cmds[3], ntos(strlen(str_rip)));
+	RESTRING(gtd->cmds[4], ntos(cnt));
+
+	str_sub = str_alloc(strlen(arg) + STRING_SIZE);
+
+	substitute(ses, arg, str_sub, SUB_CMD);
+
+	show_message(ses, LIST_COMMAND, "#SCAN BULK: FILE {%s} SCANNED.", filename);
+
+	DEL_BIT(ses->flags, SES_FLAG_SCAN);
+
+	ses = script_driver(ses, LIST_COMMAND, str_sub);
+
+	return ses;
+}
+
 struct session *scan_csv_file(struct session *ses, FILE *fp, char *filename)
 {
 	char line[STRING_SIZE], temp[BUFFER_SIZE], *arg;
@@ -194,7 +230,7 @@ char *get_arg_stop_tabs(struct session *ses, char *string, char *result, int fla
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -388,8 +424,11 @@ DO_COMMAND(do_scan)
 
 	SET_BIT(ses->flags, SES_FLAG_SCAN);
 
-
-	if (is_abbrev(arg1, "CSV"))
+	if (is_abbrev(arg1, "FILE"))
+	{
+		ses = scan_bulk_file(ses, fp, arg2, arg);
+	}
+	else if (is_abbrev(arg1, "CSV"))
 	{
 		ses = scan_csv_file(ses, fp, arg2);
 	}
@@ -405,7 +444,7 @@ DO_COMMAND(do_scan)
 	{
 		DEL_BIT(ses->flags, SES_FLAG_SCAN);
 
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCAN {ABORT|CSV|TXT} {<FILENAME>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCAN {ABORT|CSV|FILE|TSV|TXT} {<FILENAME>}");
 	}
 
 	DEL_BIT(ses->flags, SES_FLAG_SCAN);

+ 569 - 171
src/screen.c

@@ -139,7 +139,7 @@ DO_SCREEN(screen_clear)
 
 	if (is_abbrev(arg1, "ALL"))
 	{
-		printf("\e[2J");
+		print_stdout("\e[2J");
 	}
 	else if (is_abbrev(arg1, "BOTTOM SPLIT"))
 	{
@@ -149,6 +149,14 @@ DO_SCREEN(screen_clear)
 	{
 		erase_top_region(ses);
 	}
+	else if (is_abbrev(arg1, "LEFT SPLIT"))
+	{
+		erase_left_region(ses);
+	}
+	else if (is_abbrev(arg1, "RIGHT SPLIT"))
+	{
+		erase_right_region(ses);
+	}
 	else if (is_abbrev(arg1, "SCROLL REGION"))
 	{
 		erase_scroll_region(ses);
@@ -183,7 +191,7 @@ DO_SCREEN(screen_clear)
 		{
 			erase_square(ses, top_row, top_col, bot_row, bot_col);
 
-//			printf("\e[%d;%d;%d;%d${", top_row, top_col, bot_row, bot_col); VT400 not widely supported
+//			print_stdout("\e[%d;%d;%d;%d${", top_row, top_col, bot_row, bot_col); VT400 not widely supported
 		}
 	}
 	else
@@ -192,52 +200,215 @@ DO_SCREEN(screen_clear)
 	}
 }
 
-DO_SCREEN(screen_focus)
+DO_SCREEN(screen_fill)
 {
-	screen_csit(ses, "5", "", "");
-}
+	if (is_abbrev(arg1, "DEFAULT"))
+	{
+		char buf[BUFFER_SIZE];
 
+		sprintf(buf, "CLEAR ALL");
 
-DO_SCREEN(screen_rescale)
-{
-	if (is_abbrev(arg1, "HORIZONTALLY"))
-	{
-		if (*arg2 == 0)
+		do_screen(ses, buf);
+
+		if (ses->split->sav_top_row > 0)
 		{
-			screen_csit(ses, "4", "", arg2);
+			if (ses->split->sav_top_row == 1)
+			{
+				sprintf(buf, "LINE %d %d %d %d", 1, 1, ses->split->top_row - 1, gtd->screen->cols);
+			}
+			else
+			{
+				sprintf(buf, "BOX %d %d %d %d {}", 1, 1, ses->split->top_row - 1, gtd->screen->cols);
+			}
+			do_draw(ses, buf);
 		}
-		else
+
+		if (ses->split->sav_bot_row)
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {HORIZONTALLY} {[WIDTH]}");
+			if (ses->split->sav_bot_row == 1)
+			{
+				sprintf(buf, "LINE %d %d %d %d", ses->split->bot_row + 1, 1, gtd->screen->rows - 1, gtd->screen->cols);
+			}
+			else
+			{
+				sprintf(buf, "BOX %d %d %d %d {}", ses->split->bot_row + 1, 1, gtd->screen->rows - 1, gtd->screen->cols);
+			}
+			do_draw(ses, buf);
 		}
-	}
-	else if (is_abbrev(arg1, "VERTICALLY"))
-	{
-		if (*arg2 == 0)
+
+		if (ses->split->sav_top_row > 0)
 		{
-			screen_csit(ses, "4", arg2, "");
+			if (ses->split->sav_top_col)
+			{
+				sprintf(buf, "TEED VERTICAL LINE %d %d %d %d", ses->split->top_row - 1, ses->split->top_col - 1, ses->split->bot_row + 1, ses->split->top_col - 1);
+				do_draw(ses, buf);
+			}
+
+			if (ses->split->sav_bot_col)
+			{
+				sprintf(buf, "TEED VERTICAL LINE %d %d %d %d", ses->split->top_row - 1, ses->split->bot_col + 1, ses->split->bot_row + 1, ses->split->bot_col + 1);
+				do_draw(ses, buf);
+			}
 		}
 		else
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {VERTICALLY} {[HEIGHT]}");
+			if (ses->split->sav_top_col)
+			{
+				sprintf(buf, "VERTICAL LINE %d %d %d %d", ses->split->top_row, ses->split->top_col - 1, ses->split->bot_row, ses->split->top_col - 1);
+				do_draw(ses, buf);
+			}
+
+			if (ses->split->sav_bot_col)
+			{
+				sprintf(buf, "VERTICAL LINE %d %d %d %d", ses->split->top_row, ses->split->bot_col + 1, ses->split->bot_row, ses->split->bot_col + 1);
+				do_draw(ses, buf);
+			}
 		}
 	}
-	else if (*arg1 == 0 || is_math(ses, arg1))
+	else if (is_abbrev(arg1, "SPLIT"))
 	{
-		if (*arg2 == 0 || is_math(ses, arg2))
-		{
-			screen_csit(ses, "4", arg1, arg2);
-		}
-		else
-		{
-			screen_rescale(ses, 0, arg, "SYNTAX", "");
-		}
+		fill_split_region(ses, arg2);
+	}
+	else if (is_abbrev(arg1, "BOTTOM SPLIT"))
+	{
+		fill_bot_region(ses, arg2);
+	}
+	else if (is_abbrev(arg1, "TOP SPLIT"))
+	{
+		fill_top_region(ses, arg2);
+	}
+	else if (is_abbrev(arg1, "LEFT SPLIT"))
+	{
+		fill_left_region(ses, arg2);
+	}
+	else if (is_abbrev(arg1, "RIGHT SPLIT"))
+	{
+		fill_right_region(ses, arg2);
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {[HEIGHT]} {[WIDTH]}");
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {VERTICALLY} {<HEIGHT>}");
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {HORIZONTALLY} {<WIDTH>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN FILL {TOP|BOT|LEFT|RIGHT|SCROLL|SPLIT|DEFAULT} {arg}");
+	}
+}
+
+DO_SCREEN(screen_focus)
+{
+	screen_csit(ses, "5", "", "");
+}
+
+DO_SCREEN(screen_fullscreen)
+{
+	if (*arg1 == 0)
+	{
+		screen_csit(ses, "10", "2", "");
+	}
+	else if (is_abbrev(arg1, "ON"))
+	{
+		screen_csit(ses, "10", "1", "");
+	}
+	else if (is_abbrev(arg1, "OFF"))
+	{
+		screen_csit(ses, "10", "0", "");
+	}
+}
+
+DO_SCREEN(screen_get)
+{
+	if (*arg1 == 0 || *arg2 == 0)
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {GET} {FOCUS} {<VAR>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {GET} {ROWS|COLS|HEIGHT|WIDTH} {<VAR>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {GET} {CHAR_HEIGHT|CHAR_WIDTH}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {GET} {SPLIT_TOP_BAR|SPLIT_BOT_BAR|SPLIT_LEFT_BAR|SPLIT_RIGHT_BAR} {<VAR>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {GET} {SCROLL_TOP_ROW|SCROLL_TOP_COL|SCROLL_BOT_ROW|SCROLL_BOT_COL} {<VAR>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {GET} {CUR_ROW|CUR_COL} {<VAR>}");
+
+		return;
+	}
+
+	if (is_abbrev(arg1, "FOCUS"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->focus);
+	}
+	else if (is_abbrev(arg1, "CHAR_HEIGHT"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->char_height);
+	}
+	else if (is_abbrev(arg1, "CHAR_WIDTH"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->char_width);
+	}
+	else if (is_abbrev(arg1, "COLS"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->cols);
+	}
+	else if (is_abbrev(arg1, "CUR_COL"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->cur_col);
+	}
+	else if (is_abbrev(arg1, "CUR_ROW"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->cur_row);
+	}
+	else if (is_abbrev(arg1, "HEIGHT"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->height);
+	}
+	else if (is_abbrev(arg1, "WIDTH"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->width);
+	}
+
+	else if (is_abbrev(arg1, "ROWS"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->rows);
+	}
+
+	else if (is_abbrev(arg1, "SPLIT_TOP_BAR"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->sav_top_row);
+	}
+	else if (is_abbrev(arg1, "SPLIT_LEFT_BAR"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->sav_top_col);
+	}
+	else if (is_abbrev(arg1, "SPLIT_BOT_BAR"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->sav_bot_row);
+	}
+	else if (is_abbrev(arg1, "SPLIT_RIGHT_BAR"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->sav_bot_col);
+	}
+
+	else if (is_abbrev(arg1, "SCROLL_ROWS"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->bot_row - ses->split->top_row);
+	}
+	else if (is_abbrev(arg1, "SCROLL_COLS"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->wrap);
+	}
+
+	else if (is_abbrev(arg1, "SCROLL_TOP_ROW"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->top_row);
+	}
+	else if (is_abbrev(arg1, "SCROLL_TOP_COL"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->top_col);
+	}
+	else if (is_abbrev(arg1, "SCROLL_BOT_ROW"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->bot_row);
+	}
+	else if (is_abbrev(arg1, "SCROLL_BOT_COL"))
+	{
+		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->bot_col);
+	}
+	else
+	{
+		screen_get(ses, 0, "", "", "");
 	}
 }
 
@@ -308,6 +479,49 @@ DO_SCREEN(screen_move)
 	screen_csit(ses, "3", arg2, arg1); // reverse x,y to row,col
 }
 
+DO_SCREEN(screen_rescale)
+{
+	if (is_abbrev(arg1, "HORIZONTALLY"))
+	{
+		if (*arg2 == 0)
+		{
+			screen_csit(ses, "4", "", arg2);
+		}
+		else
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {HORIZONTALLY} {[WIDTH]}");
+		}
+	}
+	else if (is_abbrev(arg1, "VERTICALLY"))
+	{
+		if (*arg2 == 0)
+		{
+			screen_csit(ses, "4", arg2, "");
+		}
+		else
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {VERTICALLY} {[HEIGHT]}");
+		}
+	}
+	else if (*arg1 == 0 || is_math(ses, arg1))
+	{
+		if (*arg2 == 0 || is_math(ses, arg2))
+		{
+			screen_csit(ses, "4", arg1, arg2);
+		}
+		else
+		{
+			screen_rescale(ses, 0, arg, "SYNTAX", "");
+		}
+	}
+	else
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {[HEIGHT]} {[WIDTH]}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {VERTICALLY} {<HEIGHT>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {HORIZONTALLY} {<WIDTH>}");
+	}
+}
+
 DO_SCREEN(screen_pixel_resize)
 {
 	screen_csit(ses, "4", arg1, arg2);
@@ -334,6 +548,52 @@ DO_SCREEN(screen_scrollbar)
 	}
 }
 
+DO_SCREEN(screen_scrollregion)
+{
+	int top_row, top_col, bot_row, bot_col;
+
+	if ((*arg1 && !is_math(ses, arg1)) || (*arg2 && !is_math(ses, arg2)))
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN SCROLLREGION {TOP ROW} {TOP COL} {BOT ROW} {BOT COL}");
+
+		return;
+	}
+
+	top_row = get_number(ses, arg1);
+	top_col = get_number(ses, arg2);
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+
+	if ((*arg1 && !is_math(ses, arg1)) || (*arg2 && !is_math(ses, arg2)))
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN SCROLL {TOP ROW} {TOP COL} {BOT ROW} {BOT COL}");
+
+		return;
+	}
+
+	bot_row = get_number(ses, arg1);
+	bot_col = get_number(ses, arg2);
+
+	if ((top_row|top_col|bot_row|bot_col) == 0)
+	{
+		do_unsplit(ses, "");
+
+		return;
+	}
+
+	ses->split->sav_top_row = top_row;
+	ses->split->sav_top_col = top_col;
+	ses->split->sav_bot_row = bot_row;
+	ses->split->sav_bot_col = bot_col;
+
+	SET_BIT(ses->flags, SES_FLAG_SPLIT);
+	SET_BIT(ses->flags, SES_FLAG_SCROLLSPLIT);
+
+	init_split(ses, ses->split->sav_top_row, ses->split->sav_top_col, ses->split->sav_bot_row, ses->split->sav_bot_col);
+
+	return;
+}
 
 DO_SCREEN(screen_load)
 {
@@ -341,7 +601,7 @@ DO_SCREEN(screen_load)
 	{
 		screen_csit(ses, "23", "1", "");
 	}
-	else if (is_abbrev(arg1, "BOTH"))
+	else if (is_abbrev(arg1, "BOTH") || is_abbrev(arg1, "NAME"))
 	{
 		screen_csit(ses, "23", "0", "");
 	}
@@ -351,13 +611,17 @@ DO_SCREEN(screen_load)
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {LOAD} {BOTH|LABEL|TITLE}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {LOAD} {LABEL|NAME|TITLE}");
 	}
 }
 
 DO_SCREEN(screen_refresh)
 {
-	screen_csit(ses, "7", "", "");
+	SET_BIT(ses->flags, SES_FLAG_PRINTBUFFER);
+
+	buffer_end(ses, "");
+
+	DEL_BIT(ses->flags, SES_FLAG_PRINTBUFFER);
 }
 
 DO_SCREEN(screen_resize)
@@ -425,89 +689,29 @@ DO_SCREEN(screen_save)
 
 DO_SCREEN(screen_set)
 {
-	if (is_abbrev(arg1, "LABEL"))
-	{
-		screen_osc("1", arg2);
-	}
-	else if (is_abbrev(arg1, "BOTH"))
+	if (is_abbrev(arg1, "BOTH") || is_abbrev(arg1, "NAME"))
 	{
 		screen_osc("0", arg2);
 	}
-	else if (is_abbrev(arg1, "TITLE"))
-	{
-		screen_osc("2", arg2);
-	}
-	else
-	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SET} {BOTH|LABEL|TITLE}");
-	}
-}
-
-DO_SCREEN(screen_fullscreen)
-{
-	if (*arg1 == 0)
-	{
-		screen_csit(ses, "10", "2", "");
-	}
-	else if (is_abbrev(arg1, "ON"))
-	{
-		screen_csit(ses, "10", "1", "");
-	}
-	else if (is_abbrev(arg1, "OFF"))
-	{
-		screen_csit(ses, "10", "0", "");
-	}
-}
-
-DO_SCREEN(screen_get)
-{
-	if (*arg1 == 0 || *arg2 == 0)
-	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {GET} {FOCUS|ROWS|COLS|HEIGHT|WIDTH} {<VAR>}");
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {GET} {TOP_ROW|BOT_ROW|TOP_SPLIT|BOT_SPLIT} {<VAR>}");
-
-		return;
-	}
-
-	if (is_abbrev(arg1, "FOCUS"))
-	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->focus);
-	}
-	else if (is_abbrev(arg1, "ROWS"))
-	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->rows);
-	}
 	else if (is_abbrev(arg1, "COLS"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->cols);
-	}
-	else if (is_abbrev(arg1, "HEIGHT"))
-	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->height);
-	}
-	else if (is_abbrev(arg1, "WIDTH"))
-	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->width);
+		gtd->screen->cols = get_number(ses, arg2);
 	}
-	else if (is_abbrev(arg1, "TOP_ROW"))
+	else if (is_abbrev(arg1, "LABEL"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->top_row);
-	}
-	else if (is_abbrev(arg1, "TOP_SPLIT"))
-	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->top_split);
+		screen_osc("1", arg2);
 	}
-	else if (is_abbrev(arg1, "BOT_ROW"))
+	else if (is_abbrev(arg1, "ROWS"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->bot_row);
+		gtd->screen->rows = get_number(ses, arg2);
 	}
-	else if (is_abbrev(arg1, "BOT_SPLIT"))
+	else if (is_abbrev(arg1, "TITLE"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->bot_split);
+		screen_osc("2", arg2);
 	}
 	else
 	{
-		screen_get(ses, 0, "", "", "");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SET} {COLS|ROWS|LABEL|NAME|TITLE}");
 	}
 }
 
@@ -516,11 +720,12 @@ DO_SCREEN(screen_raise)
 	if (*arg1 == 0)
 	{
 		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN CHARACTER DIMENSIONS}");
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN POSITION}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN DESKTOP DIMENSIONS}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN DIMENSIONS}");
 		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN MINIMIZED}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN POSITION}");
 		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN RESIZE}");
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN DIMENSIONS}");
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN DESKTOP DIMENSIONS}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN SIZE}");
 
 		return;
 	}
@@ -550,6 +755,10 @@ DO_SCREEN(screen_raise)
 	{
 		check_all_events(NULL, SUB_ARG, 0, 4, "SCREEN RESIZE", ntos(gtd->screen->rows), ntos(gtd->screen->cols), ntos(gtd->screen->height), ntos(gtd->screen->width));
 	}
+	else if (is_abbrev(arg1, "SIZE"))
+	{
+		screen_csit(ses, "18", "", "");
+	}
 	else if (is_abbrev(arg1, "DIMENSIONS"))
 	{
 		screen_csit(ses, "14", "2", "");
@@ -658,12 +867,35 @@ void csit_handler(int ind, int var1, int var2)
 			msdp_update_all("SCREEN_CHARACTER_HEIGHT", "%d", gtd->screen->char_height);
 			msdp_update_all("SCREEN_CHARACTER_WIDTH", "%d", gtd->screen->char_width);
 			break;
+
+		case 7:
+			init_screen(gtd->screen->rows, gtd->screen->cols, gtd->screen->height, gtd->screen->width);
+			check_all_events(NULL, SUB_ARG, 0, 4, "SCREEN REFRESH", ntos(gtd->screen->rows), ntos(gtd->screen->cols), ntos(gtd->screen->height), ntos(gtd->screen->width));
+			break;
+
+		case 8:
+			gtd->screen->rows = var1;
+			gtd->screen->cols = var2;
+
+			check_all_events(NULL, SUB_ARG, 0, 2, "SCREEN SIZE", ntos(var1), ntos(var2));
+			msdp_update_all("SCREEN_ROWS", "%d", gtd->screen->rows);
+			msdp_update_all("SCREEN_COLS", "%d", gtd->screen->cols);
+			break;
+
+		case 9:
+			gtd->screen->desk_rows = var1;
+			gtd->screen->desk_cols = var2;
+
+			check_all_events(NULL, SUB_ARG, 0, 2, "SCREEN DESKTOP SIZE", ntos(var1), ntos(var2));
+			msdp_update_all("SCREEN_DESKTOP_ROWS", "%d", gtd->screen->desk_rows);
+			msdp_update_all("SCREEN_DESKTOP_COLS", "%d", gtd->screen->desk_cols);
+			break;
 	}
 }
 
 void screen_osc(char *arg1, char *arg2)
 {
-	printf("\e]%s;%s\a", arg1, arg2);
+	print_stdout("\e]%s;%s\a", arg1, arg2);
 }
 
 void osc_handler(char ind, char *arg)
@@ -674,14 +906,14 @@ void osc_handler(char ind, char *arg)
 void screen_csi(char *cmd, char *num1, char *num2, char *num3, char *tc)
 {
 
-	printf("\e[%s%s%s%s%s%s%s",
+	print_stdout("\e[%s%s%s%s%s%s%s",
 		cmd,
 		*num1 ? XT_S : XT_V, *num1 && *num1 != ' ' ? num1 : "",
 		*num2 ? XT_S : XT_V, *num2 && *num2 != ' ' ? num2 : "",
 		num3,
 		tc);
 
-	fflush(NULL);
+	SET_BIT(gtd->flags, TINTIN_FLAG_FLUSH);
 }
 
 void screen_csit(struct session *ses, char *arg1, char *arg2, char *arg3)
@@ -706,40 +938,67 @@ void screen_csit(struct session *ses, char *arg1, char *arg2, char *arg3)
 		strcpy(num2, arg3);
 	}
 
-	printf("\e[%s%s%s%s%st", arg1, *num1 ? XT_S : XT_V, *num1 && *num1 != ' ' ? num1 : "", *num2 ? XT_S : XT_V, *num2 && *num2 != ' ' ? num2 : "");
+	print_stdout("\e[%s%s%s%s%st", arg1, *num1 ? XT_S : XT_V, *num1 && *num1 != ' ' ? num1 : "", *num2 ? XT_S : XT_V, *num2 && *num2 != ' ' ? num2 : "");
 
 //	convert_meta(buf, debug, FALSE);
 
 //	tintin_printf2(gtd->ses, "\e[1;32m[%s] num1 (%s) num2 (%s) %s %s", num1, num2, debug, buf);
 
 
-	fflush(NULL);
+	SET_BIT(gtd->flags, TINTIN_FLAG_FLUSH);
 }
 
 
 DO_SCREEN(screen_info)
 {
-	tintin_printf2(ses, "gtd->ses->top_row:     %4d", gtd->ses->top_row);
-	tintin_printf2(ses, "gtd->ses->bot_row:     %4d", gtd->ses->bot_row);
-	tintin_printf2(ses, "gtd->ses->cur_row:     %4d", gtd->ses->cur_row);
-	tintin_printf2(ses, "gtd->ses->cur_col:     %4d", gtd->ses->cur_col);
+	int lvl;
+
+	tintin_printf2(ses, "gtd->ses->split->sav_top_row: %4d", gtd->ses->split->sav_top_row);
+	tintin_printf2(ses, "gtd->ses->split->sav_top_col: %4d", gtd->ses->split->sav_top_col);
+	tintin_printf2(ses, "gtd->ses->split->sav_bot_row: %4d", gtd->ses->split->sav_bot_row);
+	tintin_printf2(ses, "gtd->ses->split->sav_bot_col: %4d", gtd->ses->split->sav_bot_col);
+
+	tintin_printf2(ses, "gtd->ses->split->top_row:     %4d", gtd->ses->split->top_row);
+	tintin_printf2(ses, "gtd->ses->split->top_col:     %4d", gtd->ses->split->top_col);
+	tintin_printf2(ses, "gtd->ses->split->bot_row:     %4d", gtd->ses->split->bot_row);
+	tintin_printf2(ses, "gtd->ses->split->bot_col:     %4d", gtd->ses->split->bot_col);
+
+	tintin_printf2(ses, "");
+
+	tintin_printf2(ses, "gtd->ses->wrap:           %4d", gtd->ses->wrap);
+	tintin_printf2(ses, "gtd->ses->cur_row:        %4d", gtd->ses->cur_row);
+	tintin_printf2(ses, "gtd->ses->cur_col:        %4d", gtd->ses->cur_col);
+
+	for (lvl = 0 ; lvl < gtd->screen->sav_lev ; lvl++)
+	{
+	tintin_printf2(ses, "gtd->screen->sav_row[%2d]: %4d", lvl, gtd->screen->sav_row[lvl]);
+	tintin_printf2(ses, "gtd->screen->sav_col[%2d]: %4d", lvl, gtd->screen->sav_col[lvl]);
+	}
+
+	tintin_printf2(ses, "");
+
+	tintin_printf2(ses, "gtd->screen->rows:        %4d", gtd->screen->rows);
+	tintin_printf2(ses, "gtd->screen->cols:        %4d", gtd->screen->cols);
+	tintin_printf2(ses, "gtd->screen->height:      %4d", gtd->screen->height);
+	tintin_printf2(ses, "gtd->screen->pix_cows:    %4d", gtd->screen->width);
+
+	return;
 
 	tintin_printf2(ses, "");
 
-	tintin_printf2(ses, "gtd->screen->rows:     %4d", gtd->screen->rows);
-	tintin_printf2(ses, "gtd->screen->cols:     %4d", gtd->screen->cols);
-	tintin_printf2(ses, "gtd->screen->height: %4d", gtd->screen->height);
-	tintin_printf2(ses, "gtd->screen->pix_cows: %4d", gtd->screen->width);
+	tintin_printf2(ses, "gtd->screen->top_row:     %4d", gtd->screen->top_row);
+	tintin_printf2(ses, "gtd->screen->bot_row:     %4d", gtd->screen->bot_row);
+	tintin_printf2(ses, "gtd->screen->cur_row:     %4d", gtd->screen->cur_row);
+	tintin_printf2(ses, "gtd->screen->cur_col:     %4d", gtd->screen->cur_col);
+	tintin_printf2(ses, "gtd->screen->max_row:     %4d", gtd->screen->max_row);
 
 	tintin_printf2(ses, "");
 
-	tintin_printf2(ses, "gtd->screen->top_row:  %4d", gtd->screen->top_row);
-	tintin_printf2(ses, "gtd->screen->bot_row:  %4d", gtd->screen->bot_row);
-	tintin_printf2(ses, "gtd->screen->sav_row:  %4d", gtd->screen->sav_row);
-	tintin_printf2(ses, "gtd->screen->sav_col:  %4d", gtd->screen->sav_col);
-	tintin_printf2(ses, "gtd->screen->cur_row:  %4d", gtd->screen->cur_row);
-	tintin_printf2(ses, "gtd->screen->cur_col:  %4d", gtd->screen->cur_col);
-	tintin_printf2(ses, "gtd->screen->max_row:  %4d", gtd->screen->max_row);
+
+	if (!HAS_BIT(ses->flags, SES_FLAG_READMUD) && IS_SPLIT(ses))
+	{
+		tintin_printf2(ses, "SPLIT mode detected.");
+	}
 }
 
 /*
@@ -776,15 +1035,13 @@ void init_screen(int rows, int cols, int height, int width)
 {
 	int cnt;
 
-	if (gtd->screen == NULL)
-	{
-		gtd->screen = calloc(1, sizeof(struct screen_data));
-	}
+	gtd->screen->rows        = rows;
+	gtd->screen->cols        = cols;
+	gtd->screen->height      = height;
+	gtd->screen->width       = width;
+	gtd->screen->char_height = gtd->screen->height / gtd->screen->rows;
+	gtd->screen->char_width  = gtd->screen->width / gtd->screen->cols;
 
-	gtd->screen->rows   = rows;
-	gtd->screen->cols   = cols;
-	gtd->screen->height = height;
-	gtd->screen->width  = width;
 	gtd->screen->focus  = 1;
 
 	return;
@@ -816,10 +1073,11 @@ void init_screen(int rows, int cols, int height, int width)
 	}
 
 	gtd->screen->rows = rows;
-	gtd->screen->sav_row = gtd->screen->cur_row = rows;
-
 	gtd->screen->cols = cols;
-	gtd->screen->sav_col = gtd->screen->cur_col = 0;
+
+	gtd->screen->sav_lev = 0;
+	gtd->screen->sav_row[0] = gtd->screen->cur_row = rows;
+	gtd->screen->sav_col[0] = gtd->screen->cur_col = 0;
 
 	for (cnt = 0 ; cnt < gtd->screen->max_row ; cnt++)
 	{
@@ -856,7 +1114,7 @@ void print_screen()
 
 	for (cnt = 0 ; cnt < gtd->screen->rows ; cnt++)
 	{
-		printf("%2d %s\n", cnt, gtd->screen->lines[cnt]->str);
+		print_stdout("%2d %s\n", cnt, gtd->screen->lines[cnt]->str);
 	}
 }
 
@@ -874,7 +1132,7 @@ void add_line_screen(char *str)
 
 	if (gtd->screen == NULL)
 	{
-		printf("screen == NULL!\n");
+		print_stdout("screen == NULL!\n");
 
 		pop_call();
 		return;
@@ -882,11 +1140,11 @@ void add_line_screen(char *str)
 
 	while (str)
 	{
-		cnt = gtd->ses->top_row - 1;
+		cnt = gtd->ses->split->top_row - 1;
 
 		free(gtd->screen->lines[cnt]->str);
 
-		while (cnt < gtd->ses->bot_row - 2)
+		while (cnt < gtd->ses->split->bot_row - 2)
 		{
 			gtd->screen->lines[cnt]->str = gtd->screen->lines[cnt + 1]->str;
 
@@ -947,28 +1205,15 @@ void erase_scroll_region(struct session *ses)
 {
 	int row;
 
-	push_call("erase_scroll_region(%p) [%d,%d]",ses,ses->top_row,ses->bot_row);
+	push_call("erase_scroll_region(%p) [%d,%d]",ses,ses->split->top_row,ses->split->bot_row);
 
-	if (ses->wrap <= 0)
-	{
-		save_pos(ses);
-		goto_pos(ses, ses->top_row, 1);
-		erase_lines(ses, ses->bot_row - ses->top_row);
-		load_pos(ses);
-	}
-	else
-	{
-		save_pos(ses);
-		goto_pos(ses, ses->top_row, 1);
+	save_pos(ses);
 
-		for (row = ses->top_row ; row < ses->bot_row ; row++)
-		{
-			printf("\e[%dX\n", ses->wrap);
-		}
-		load_pos(ses);
+	for (row = ses->split->top_row ; row <= ses->split->bot_row ; row++)
+	{
+		print_stdout("\e[%d;%dH\e[%dX", row, ses->split->top_col, ses->wrap);
 	}
-
-//	printf("\e[%d;%d;%d;%d${", ses->top_row, 1, ses->bot_row, gtd->screen->cols); VT400 not widely supported
+	restore_pos(ses);
 
 	pop_call();
 	return;
@@ -979,22 +1224,26 @@ void erase_split_region(struct session *ses)
 	erase_top_region(ses);
 
 	erase_bot_region(ses);
+
+	erase_left_region(ses);
+
+	erase_right_region(ses);
 }
 
 void erase_top_region(struct session *ses)
 {
 	int row;
 
-	if (ses->top_row > 1)
+	if (ses->split->top_row > 1)
 	{
 		save_pos(ses);
 		goto_pos(ses, 1, 1);
 
-		for (row = 1 ; row < ses->top_row ; row++)
+		for (row = 1 ; row < ses->split->top_row ; row++)
 		{
-			printf("\e[K\n");
+			print_stdout("\e[K\n");
 		}
-		load_pos(ses);
+		restore_pos(ses);
 	}
 }
 
@@ -1002,19 +1251,52 @@ void erase_bot_region(struct session *ses)
 {
 	int row;
 
-	if (ses->bot_row < gtd->screen->rows)
+	if (ses->split->bot_row < gtd->screen->rows)
+	{
+		save_pos(ses);
+		goto_pos(ses, ses->split->bot_row + 1, 1);
+
+		for (row = ses->split->bot_row + 1 ; row < gtd->screen->rows ; row++)
+		{
+			print_stdout("\e[K\n");
+		}
+		restore_pos(ses);
+	}
+}
+
+void erase_left_region(struct session *ses)
+{
+	int row;
+
+	if (ses->split->top_col > 1)
 	{
 		save_pos(ses);
-		goto_pos(ses, ses->bot_row + 1, 1);
 
-		for (row = ses->bot_row + 1 ; row < gtd->screen->rows ; row++)
+		for (row = ses->split->top_row ; row <= ses->split->bot_row ; row++)
 		{
-			printf("\e[K\n");
+			print_stdout("\e[%d;1H\e[%dX", row, ses->split->top_col - 1);
 		}
-		load_pos(ses);
+		restore_pos(ses);
 	}
 }
 
+void erase_right_region(struct session *ses)
+{
+	int row;
+
+	if (ses->split->bot_col < gtd->screen->cols)
+	{
+		save_pos(ses);
+
+		for (row = ses->split->top_row ; row <= ses->split->bot_row ; row++)
+		{
+			print_stdout("\e[%d;%dH\e[K", row, ses->split->bot_col + 1);
+		}
+		restore_pos(ses);
+	}
+}
+
+
 void erase_square(struct session *ses, int top_row, int top_col, int bot_row, int bot_col)
 {
 	int row;
@@ -1026,14 +1308,130 @@ void erase_square(struct session *ses, int top_row, int top_col, int bot_row, in
 	for (row = top_row ; row <= bot_row ; row++)
 	{
 		goto_pos(ses, row, top_col);
-		printf("\e[%dX", bot_col - top_col + 1);
+		print_stdout("\e[%dX", bot_col - top_col + 1);
 	}
-	load_pos(ses);
+	restore_pos(ses);
 
 	pop_call();
 	return;
 }
 
+void fill_scroll_region(struct session *ses, char *arg)
+{
+	int row, col;
+
+	save_pos(ses);
+
+	for (row = ses->split->top_row ; row < ses->split->bot_row ; row++)
+	{
+		goto_pos(ses, row, ses->split->top_col);
+
+		for (col = ses->split->top_col ; col < ses->split->bot_col ; col++)
+		{
+			print_stdout("%s", arg);
+		}
+		print_stdout("\n");
+	}
+	restore_pos(ses);
+}
+
+void fill_top_region(struct session *ses, char *arg)
+{
+	int row, col;
+
+	if (ses->split->top_row > 1)
+	{
+		save_pos(ses);
+		goto_pos(ses, 1, 1);
+
+		for (row = 1 ; row < ses->split->top_row ; row++)
+		{
+			print_stdout("\e[0m");
+
+			for (col = 0 ; col < gtd->screen->cols ; col++)
+			{
+				print_stdout("%s", arg);
+			}
+			print_stdout("\n");
+		}
+		restore_pos(ses);
+	}
+}
+
+void fill_bot_region(struct session *ses, char *arg)
+{
+	int row, col;
+
+	if (ses->split->bot_row < gtd->screen->rows)
+	{
+		save_pos(ses);
+		goto_pos(ses, ses->split->bot_row + 1, 1);
+
+		for (row = ses->split->bot_row + 1 ; row < gtd->screen->rows ; row++)
+		{
+			print_stdout("\e[0m");
+			for (col = 0 ; col < gtd->screen->cols ; col++)
+			{
+				print_stdout("%s", arg);
+			}
+			print_stdout("\n");
+		}
+		restore_pos(ses);
+	}
+}
+
+void fill_left_region(struct session *ses, char *arg)
+{
+	int row, col;
+
+	if (ses->split->top_col > 1)
+	{
+		save_pos(ses);
+
+		for (row = ses->split->top_row ; row <= ses->split->bot_row ; row++)
+		{
+			print_stdout("\e[%d;1H\e[0m", row);
+
+			for (col = 0 ; col < ses->split->top_col - 1 ; col++)
+			{
+				print_stdout("%s", arg);
+			}
+		}
+		restore_pos(ses);
+	}
+}
+
+void fill_right_region(struct session *ses, char *arg)
+{
+	int row, col;
+
+	if (ses->split->bot_col < gtd->screen->cols)
+	{
+		save_pos(ses);
+
+		for (row = ses->split->top_row ; row <= ses->split->bot_row ; row++)
+		{
+			print_stdout("\e[%d;%dH\e[0m", row, ses->split->bot_col + 1);
+
+			for (col = ses->split->bot_col + 1 ; col <= gtd->screen->cols ; col++)
+			{
+				print_stdout("%s", arg);
+			}
+		}
+		restore_pos(ses);
+	}
+}
+
+void fill_split_region(struct session *ses, char *arg)
+{
+	fill_top_region(ses, arg);
+
+	fill_bot_region(ses, arg);
+
+	fill_left_region(ses, arg);
+
+	fill_right_region(ses, arg);
+}
 
 void get_line_screen(char *result, int row)
 {

+ 51 - 19
src/session.c

@@ -76,6 +76,34 @@ DO_COMMAND(do_session)
 	}
 	else if (*arg1 && *arg == 0)
 	{
+		if (!strncasecmp(arg1, "telnet://", 9))
+		{
+			char *pti, *pto;
+
+			pto = temp;
+			pti = arg1 + 9;
+
+			while (*pti)
+			{
+				if (*pti == '/')
+				{
+					break;
+				}
+				else if (*pti == ':')
+				{
+					pti++;
+					*pto++ = ' ';
+				}
+				else
+				{
+					*pto++ = *pti++;
+				}
+			}
+			*pto = 0;
+
+			ses = new_session(ses, "telnet", temp, 0, 0);
+		}
+
 		if (*arg1 == '+')
 		{
 			return activate_session(ses->next ? ses->next : gts->next ? gts->next : ses);
@@ -221,7 +249,7 @@ void show_session(struct session *ses, struct session *ptr)
 
 	cat_sprintf(temp, " %8s", ptr == gtd->ses ? "(active)" :  "");
 
-	cat_sprintf(temp, " %10s", ptr->mccp ? ptr->mccp3 ? "(mccp 2+3)" : "(mccp 2)  " : "");
+	cat_sprintf(temp, " %10s", ptr->mccp2 ? (ptr->mccp3 ? "(mccp 2+3)" : "(mccp 2)  ") : ptr->mccp3 ? "(mccp 3)" : "");
 
 	cat_sprintf(temp, " %7s", HAS_BIT(ptr->flags, SES_FLAG_SNOOP) ? "(snoop)" : "");
 
@@ -278,7 +306,7 @@ struct session *activate_session(struct session *ses)
 
 	dirty_screen(ses);
 
-	tintin_printf(ses, "#SESSION '%s' ACTIVATED.", ses->name);
+	show_message(ses, LIST_COMMAND, "#SESSION '%s' ACTIVATED.", ses->name);
 
 	check_all_events(ses, SUB_ARG, 0, 1, "SESSION ACTIVATED", ses->name);
 
@@ -344,9 +372,14 @@ struct session *new_session(struct session *ses, char *name, char *arg, int desc
 
 	newses->group         = strdup(gts->group);
 	newses->flags         = gts->flags;
+	newses->color         = gts->color;
+	newses->logmode       = gts->logmode;
+	newses->charset       = gts->charset;
+
 	newses->telopts       = gts->telopts;
 	newses->auto_tab      = gts->auto_tab;
-
+	newses->packet_patch  = gts->packet_patch;
+	newses->tab_width     = gts->tab_width;
 	newses->cmd_color     = strdup(gts->cmd_color);
 
 	newses->read_max      = gts->read_max;
@@ -356,6 +389,7 @@ struct session *new_session(struct session *ses, char *name, char *arg, int desc
 	newses->logline_name  = strdup("");
 	newses->rand          = utime();
 
+
 	LINK(newses, gts->next, gts->prev);
 
 	if (HAS_BIT(gtd->flags, TINTIN_FLAG_INHERITANCE))
@@ -380,15 +414,17 @@ struct session *new_session(struct session *ses, char *name, char *arg, int desc
 		}
 	}
 
-	newses->top_split = gts->top_split;
-	newses->bot_split = gts->bot_split;
+	newses->split  = calloc(1, sizeof(struct split_data));
 
-	newses->top_row = gts->top_row;
-	newses->bot_row = gts->bot_row;
+	memcpy(newses->split, gts->split, sizeof(struct split_data));
+
+	newses->cur_row = gts->cur_row;
+	newses->cur_col = gts->cur_col;
 
 	newses->wrap    = gts->wrap;
 
-	init_buffer(newses, -1);
+        newses->scroll = calloc(1, sizeof(struct scroll_data));
+	init_buffer(newses, gts->scroll->size);
 
 	memcpy(&newses->cur_terminal, &gts->cur_terminal, sizeof(gts->cur_terminal));
 
@@ -407,7 +443,7 @@ struct session *new_session(struct session *ses, char *name, char *arg, int desc
 
 	dirty_screen(newses);
 
-	if (gtd->background_level == 0)
+	if (gtd->level->background == 0)
 	{
 		gtd->ses = newses;
 	}
@@ -451,23 +487,19 @@ struct session *new_session(struct session *ses, char *name, char *arg, int desc
 	}
 #endif
 
-
 	if (*file)
 	{
 		newses = do_read(newses, file);
 	}
 	check_all_events(newses, SUB_ARG, 0, 4, "SESSION CREATED", newses->name, newses->session_host, newses->session_ip, newses->session_port);
 
-	if (gtd->background_level == 0)
+	if (gtd->level->background == 0)
 	{
 		pop_call();
 		return newses;
 	}
-	else
-	{
-		pop_call();
-		return ses;
-	}
+	pop_call();
+	return ses;
 }
 
 struct session *connect_session(struct session *ses)
@@ -676,10 +708,10 @@ void dispose_session(struct session *ses)
 		delete_map(ses);
 	}
 
-	if (ses->mccp)
+	if (ses->mccp2)
 	{
-		inflateEnd(ses->mccp);
-		free(ses->mccp);
+		inflateEnd(ses->mccp2);
+		free(ses->mccp2);
 	}
 
 	init_buffer(ses, 0);

+ 31 - 58
src/show.c

@@ -49,11 +49,11 @@ DO_COMMAND(do_showme)
 	{
 		DEL_BIT(ses->flags, SES_FLAG_GAG);
 
-		gtd->ignore_level++;
+		gtd->level->ignore++;
 
 		show_info(ses, LIST_GAG, "#INFO GAG {%s}", arg1);
 
-		gtd->ignore_level--;
+		gtd->level->ignore--;
 
 		return ses;
 	}
@@ -81,7 +81,8 @@ DO_COMMAND(do_showme)
 		if (!HAS_BIT(ses->flags, SES_FLAG_READMUD) && IS_SPLIT(ses))
 		{
 			save_pos(ses);
-			goto_rowcol(ses, ses->bot_row, 1);
+
+			goto_pos(ses, ses->split->bot_row, ses->split->top_col);
 		}
 
 		print_line(ses, &output, lnf);
@@ -107,7 +108,7 @@ void show_message(struct session *ses, int index, char *format, ...)
 
 	root = ses->list[index];
 
-	if (gtd->verbose_level)
+	if (gtd->level->verbose || gtd->level->debug )
 	{
 		goto display;
 	}
@@ -122,7 +123,7 @@ void show_message(struct session *ses, int index, char *format, ...)
 		goto end;
 	}
 
-	if (gtd->input_level)
+	if (gtd->level->input)
 	{
 		goto end;
 	}
@@ -173,7 +174,7 @@ void show_error(struct session *ses, int index, char *format, ...)
 	vasprintf(&buffer, format, args);
 	va_end(args);
 
-	if (gtd->verbose_level)
+	if (gtd->level->verbose || gtd->level->debug)
 	{
 		tintin_puts2(ses, buffer);
 
@@ -222,7 +223,7 @@ void show_debug(struct session *ses, int index, char *format, ...)
 
 	root = ses->list[index];
 
-	if (!HAS_BIT(root->flags, LIST_FLAG_DEBUG) && !HAS_BIT(root->flags, LIST_FLAG_LOG))
+	if (gtd->level->debug == 0 && !HAS_BIT(root->flags, LIST_FLAG_DEBUG) && !HAS_BIT(root->flags, LIST_FLAG_LOG))
 	{
 		pop_call();
 		return;
@@ -234,13 +235,13 @@ void show_debug(struct session *ses, int index, char *format, ...)
 
 	va_end(args);
 
-	if (HAS_BIT(root->flags, LIST_FLAG_DEBUG))
+	if (gtd->level->debug || HAS_BIT(root->flags, LIST_FLAG_DEBUG))
 	{
-		gtd->verbose_level++;
+		gtd->level->verbose++;
 
 		tintin_puts2(ses, buf);
 
-		gtd->verbose_level--;
+		gtd->level->verbose--;
 
 		pop_call();
 		return;
@@ -267,7 +268,7 @@ void show_info(struct session *ses, int index, char *format, ...)
 
 	root = ses->list[index];
 
-	if (!HAS_BIT(root->flags, LIST_FLAG_INFO))
+	if (gtd->level->info == 0 && !HAS_BIT(root->flags, LIST_FLAG_INFO))
 	{
 		pop_call();
 		return;
@@ -279,11 +280,11 @@ void show_info(struct session *ses, int index, char *format, ...)
 
 	va_end(args);
 
-	gtd->verbose_level++;
+	gtd->level->verbose++;
 
 	tintin_puts(ses, buf);
 
-	gtd->verbose_level--;
+	gtd->level->verbose--;
 
 	pop_call();
 	return;
@@ -352,11 +353,19 @@ void tintin_header(struct session *ses, char *format, ...)
 	va_list args;
 	int cols;
 
+	push_call("tintin_header(%p,%p)",ses,format);
+
 	va_start(args, format);
 	vsprintf(arg, format, args);
 	va_end(args);
 
-	cols = ses->wrap > 0 ? ses->wrap : gtd->screen->cols;
+	cols = get_scroll_cols(ses);
+
+	if (cols < 2)
+	{
+		pop_call();
+		return;
+	}
 
 	if ((int) strlen(arg) > cols - 2)
 	{
@@ -377,6 +386,9 @@ void tintin_header(struct session *ses, char *format, ...)
 	buf[cols] = 0;
 
 	tintin_puts2(ses, buf);
+
+	pop_call();
+	return;
 }
 
 void tintin_printf2(struct session *ses, char *format, ...)
@@ -427,11 +439,11 @@ void tintin_puts(struct session *ses, char *string)
 	{
 		DEL_BIT(ses->flags, SES_FLAG_GAG);
 
-		gtd->ignore_level++;
+		gtd->level->ignore++;
 
 		show_info(ses, LIST_GAG, "#INFO GAG {%s}", string);
 
-		gtd->ignore_level--;
+		gtd->level->ignore--;
 	}
 	else
 	{
@@ -453,46 +465,6 @@ void tintin_puts2(struct session *ses, char *string)
 
 	tintin_puts3(ses, output);
 
-/*
-
-	if (ses == NULL)
-	{
-		ses = gtd->ses;
-	}
-
-	if (!HAS_BIT(gtd->ses->flags, SES_FLAG_VERBOSE) && gtd->quiet_level && gtd->verbose_level == 0)
-	{
-		pop_call();
-		return;
-	}
-
-	if (strip_vt102_strlen(ses, ses->more_output) != 0)
-	{
-		output = str_dup_printf("\n\e[0m%s\e[0m", string);
-	}
-	else
-	{
-		output = str_dup_printf("\e[0m%s\e[0m", string);
-	}
-
-	add_line_buffer(ses, output, FALSE);
-
-	if (ses == gtd->ses)
-	{
-		if (!HAS_BIT(ses->flags, SES_FLAG_READMUD) && IS_SPLIT(ses))
-		{
-			save_pos(ses);
-			goto_rowcol(ses, ses->bot_row, 1);
-		}
-
-		print_line(ses, &output, FALSE);
-
-		if (!HAS_BIT(ses->flags, SES_FLAG_READMUD) && IS_SPLIT(ses))
-		{
-			restore_pos(ses);
-		}
-	}
-*/
 	str_free(output);
 
 	pop_call();
@@ -516,7 +488,7 @@ void tintin_puts3(struct session *ses, char *string)
 		ses = gtd->ses;
 	}
 
-	if (!HAS_BIT(gtd->ses->flags, SES_FLAG_VERBOSE) && gtd->quiet_level && gtd->verbose_level == 0)
+	if (!HAS_BIT(gtd->ses->flags, SES_FLAG_VERBOSE) && gtd->level->quiet && gtd->level->verbose == 0)
 	{
 		pop_call();
 		return;
@@ -538,7 +510,8 @@ void tintin_puts3(struct session *ses, char *string)
 		if (!HAS_BIT(ses->flags, SES_FLAG_READMUD) && IS_SPLIT(ses))
 		{
 			save_pos(ses);
-			goto_rowcol(ses, ses->bot_row, 1);
+
+			goto_pos(ses, ses->split->bot_row, ses->split->top_col);
 		}
 
 		print_line(ses, &output, FALSE);

+ 114 - 95
src/split.c

@@ -27,72 +27,54 @@
 
 #include "tintin.h"
 
-
 DO_COMMAND(do_split)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
-	int top, bot;
-
-	if (arg == NULL)
-	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SPLIT {[TOP ROWS]} {[BOTTOM ROWS]}");
-
-		return ses;
-	}
 
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
 
-	if (*arg1 == 0)
+	if ((*arg1 && !is_math(ses, arg1)) || (*arg2 && !is_math(ses, arg2)))
 	{
-		if (*arg2 == 0)
-		{
-			top = 0;
-			bot = 1;
-		}
-		else if (is_math(ses, arg2))
+		if (*arg == 0)
 		{
-			top = 0;
-			bot = get_number(ses, arg2);
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SPLIT {TOP BAR} {BOTTOM BAR}");
 		}
 		else
 		{
-			return do_split(ses, NULL);
-		}
-	}
-	else if (*arg2 == 0)
-	{
-		if (is_math(ses, arg1))
-		{
-			top = 0;
-			bot = get_number(ses, arg1);
-		}
-		else
-		{
-			return do_split(ses, NULL);
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SPLIT {TOP BAR} {BOT BAR} {LEFT BAR} {RIGHT BAR}");
 		}
+		return ses;
 	}
-	else if (!is_math(ses, arg1) || !is_math(ses, arg2))
+
+	ses->split->sav_top_row = *arg1 ? get_number(ses, arg1) : 0;
+	ses->split->sav_bot_row = *arg2 ? get_number(ses, arg2) : 1;
+
+	if (*arg == 0)
 	{
-		return do_split(ses, NULL);
+		ses->split->sav_top_col = 0;
+		ses->split->sav_bot_col = 0;
 	}
 	else
 	{
-		top = get_number(ses, arg1);
-		bot = get_number(ses, arg2);
-	}
+		arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+		arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
 
-	if (top < 0 || bot < 0)
-	{
-		show_error(ses, LIST_COMMAND, "#ERROR: NEGATIVE VALUE(S): #SPLIT {%d} {%d}", top, bot);
+		if ((*arg1 && !is_math(ses, arg1)) || (*arg2 && !is_math(ses, arg2)))
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SPLIT {TOP BAR} {BOT BAR} {LEFT BAR} {RIGHT BAR}");
 
-		return ses;
+			return ses;
+		}
+
+		ses->split->sav_top_col = *arg1 ? get_number(ses, arg1) : 1;
+		ses->split->sav_bot_col = *arg2 ? get_number(ses, arg2) : 0;
 	}
 
-	ses->top_split = top;
-	ses->bot_split = bot;
+	DEL_BIT(ses->flags, SES_FLAG_SCROLLSPLIT);
+	SET_BIT(ses->flags, SES_FLAG_SPLIT);
 
-	init_split(ses, 1 + ses->top_split, gtd->screen->rows - 1 - ses->bot_split);
+	init_split(ses, ses->split->sav_top_row, ses->split->sav_top_col, ses->split->sav_bot_row,  ses->split->sav_bot_col);
 
 	return ses;
 }
@@ -100,8 +82,14 @@ DO_COMMAND(do_split)
 
 DO_COMMAND(do_unsplit)
 {
+	memset(ses->split, 0, sizeof(struct split_data));
+
+	ses->wrap = gtd->screen->cols;
+
 	reset_screen(ses);
 
+	SET_BIT(ses->scroll->flags, SCROLL_FLAG_RESIZE);
+
 	if (HAS_BIT(ses->flags, SES_FLAG_SPLIT))
 	{
 		if (HAS_BIT(ses->telopts, TELOPT_FLAG_NAWS))
@@ -109,51 +97,66 @@ DO_COMMAND(do_unsplit)
 			client_send_sb_naws(ses, 0, NULL);
 		}
 		DEL_BIT(ses->flags, SES_FLAG_SPLIT);
+		DEL_BIT(ses->flags, SES_FLAG_SCROLLSPLIT);
 	}
+	check_all_events(ses, SUB_ARG, 0, 4, "SCREEN UNSPLIT", ntos(ses->split->top_row), ntos(ses->split->top_col), ntos(ses->split->bot_row), ntos(ses->split->bot_col));
 	return ses;
 }
 
-void init_split(struct session *ses, int top, int bot)
+void init_split(struct session *ses, int top_row, int top_col, int bot_row, int bot_col)
 {
-	push_call("init_split(%p,%d,%d)",ses,top,bot);
+	push_call("init_split(%p,%d,%d,%d,%d)",ses,top_row,top_col,bot_row,bot_col);
 
-	if (bot > gtd->screen->rows)
+	if (!HAS_BIT(ses->flags, SES_FLAG_SPLIT))
 	{
-		bot = gtd->screen->rows;
-	}
+		ses->split->top_row = 1;
+		ses->split->top_col = 1;
+		ses->split->bot_row = gtd->screen->rows;
+		ses->split->bot_col = gtd->screen->cols;
+		ses->wrap = gtd->screen->cols;
 
-	if (bot < 2)
-	{
-		bot = 2;
+		init_pos(ses, gtd->screen->rows, 1);
+
+		SET_BIT(ses->scroll->flags, SCROLL_FLAG_RESIZE);
+
+		if (ses->map && HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP))
+		{
+			SET_BIT(ses->flags, SES_FLAG_UPDATEVTMAP);
+		}
+
+		pop_call();
+		return;
 	}
 
-	if (top >= bot)
+	if (HAS_BIT(ses->flags, SES_FLAG_SCROLLSPLIT))
 	{
-		top = bot - 1;
+		ses->split->top_row = top_row > 0 ? top_row : top_row < 0 ? gtd->screen->rows + top_row + 1 : 1;
+		ses->split->top_col = top_col > 0 ? top_col : top_col < 0 ? gtd->screen->cols + top_col + 1 : 1;
+		ses->split->bot_row = bot_row > 0 ? bot_row : bot_row < 0 ? gtd->screen->rows + bot_row + 1 : gtd->screen->rows - 2;
+		ses->split->bot_col = bot_col > 0 ? bot_col : bot_col < 0 ? gtd->screen->cols + bot_col + 1 : gtd->screen->cols;
 	}
-
-	if (top < 1)
+	else
 	{
-		top = 1;
+		ses->split->top_row = top_row > 0 ? top_row + 1 : top_row < 0 ? gtd->screen->rows + top_row + 1 : 1;
+		ses->split->top_col = top_col > 0 ? top_col + 1 : top_col < 0 ? gtd->screen->cols + top_col + 1 : 1;
+
+		ses->split->bot_row = bot_row > 0 ? gtd->screen->rows - bot_row - 1 : bot_row < 0 ? bot_row * -1 : gtd->screen->rows - 1;
+		ses->split->bot_col = bot_col > 0 ? gtd->screen->cols - bot_col : bot_col < 0 ? bot_col * -1 : gtd->screen->cols;
 	}
 
-	scroll_region(ses, top, bot);
+	ses->split->top_row = URANGE(1, ses->split->top_row, gtd->screen->rows -3);
+	ses->split->bot_row = URANGE(ses->split->top_row + 1,  ses->split->bot_row, gtd->screen->rows - 1);
 
-	SET_BIT(ses->flags, SES_FLAG_SPLIT);
+	ses->split->top_col = URANGE(1, ses->split->top_col, gtd->screen->cols - 2);
+	ses->split->bot_col = URANGE(ses->split->top_col + 1, ses->split->bot_col, gtd->screen->cols);
 
-	for (bot = 1 ; gtd->screen->rows - bot > ses->bot_row ; bot++)
-	{
-		split_show(ses, "", gtd->screen->rows - bot, 0);
-	}
+	ses->wrap = ses->split->bot_col - (ses->split->top_col - 1);
 
-	set_line_screen("", ses->bot_row - 1, 0);
+	scroll_region(ses, ses->split->top_row, ses->split->bot_row);
 
-	for (top = 1 ; top < ses->top_row ; top++)
-	{
-		split_show(ses, "", top, 0);
-	}
+	init_pos(ses, gtd->screen->rows, 1);
 
-	goto_rowcol(ses, gtd->screen->rows, 1);
+	SET_BIT(ses->scroll->flags, SCROLL_FLAG_RESIZE);
 
 	if (HAS_BIT(ses->telopts, TELOPT_FLAG_NAWS))
 	{
@@ -165,7 +168,23 @@ void init_split(struct session *ses, int top, int bot)
 		SET_BIT(ses->flags, SES_FLAG_UPDATEVTMAP);
 	}
 
-	buffer_end(ses, "");
+	if (!HAS_BIT(ses->flags, SES_FLAG_SCROLLSPLIT))
+	{
+		if (gtd->level->quiet == 0)
+		{
+			if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
+			{
+				do_screen(ses, "FILL DEFAULT");
+			}
+			else
+			{
+				erase_scroll_region(ses);
+				buffer_end(ses, "");
+				fill_split_region(ses, "-");
+			}
+		}
+	}
+	check_all_events(ses, SUB_ARG, 0, 4, "SCREEN SPLIT", ntos(ses->split->top_row), ntos(ses->split->top_col), ntos(ses->split->bot_row), ntos(ses->split->bot_col));
 
 	pop_call();
 	return;
@@ -180,10 +199,7 @@ void reset_screen(struct session *ses)
 {
 	reset_scroll_region(ses);
 
-	if (ses == gtd->ses)
-	{
-		goto_rowcol(ses, gtd->screen->rows, 1);
-	}
+	init_pos(ses, gtd->screen->rows, 1);
 }
 
 
@@ -197,15 +213,15 @@ void dirty_screen(struct session *ses)
 
 	refresh_session_terminal(ses);
 
-	printf("\e=");
+	print_stdout("\e=");
 
 	if (HAS_BIT(ses->flags, SES_FLAG_SPLIT))
 	{
-		init_split(ses, 1 + ses->top_split, gtd->screen->rows - 1 - ses->bot_split);
+		init_split(ses, ses->split->sav_top_row, ses->split->sav_top_col, ses->split->sav_bot_row, ses->split->sav_bot_col);
 	}
 	else if (IS_SPLIT(ses))
 	{
-		scroll_region(ses, ses->top_row, ses->bot_row);
+		scroll_region(ses, ses->split->top_row, ses->split->bot_row);
 	}
 	else
 	{
@@ -214,11 +230,9 @@ void dirty_screen(struct session *ses)
 
 	if (IS_SPLIT(ses) && ses == gtd->ses)
 	{
-		goto_rowcol(ses, gtd->screen->rows, 1);
+		init_pos(ses, gtd->screen->rows, 1);
 	}
 
-//	buffer_end(ses, "");
-
 	pop_call();
 	return;
 }
@@ -267,11 +281,13 @@ void split_show(struct session *ses, char *prompt, int row, int col)
 		return;
 	}
 
-	if (row > ses->top_row && row < ses->bot_row)
+	if (row >= ses->split->top_row && row <= ses->split->bot_row)
 	{
-		show_error(ses, LIST_PROMPT, "#ERROR: PROMPT ROW IS INSIDE THE SCROLLING REGION: {%s} {%d}.", prompt, original_row);
-
-		return;
+		if (col >= ses->split->top_col && col <= ses->split->bot_col)
+		{
+			show_error(ses, LIST_PROMPT, "#ERROR: PROMPT ROW IS INSIDE THE SCROLLING REGION: {%s} {%d}.", prompt, original_row);
+			return;
+		}
 	}
 
 	if (ses != gtd->ses)
@@ -296,27 +312,30 @@ void split_show(struct session *ses, char *prompt, int row, int col)
 		sprintf(temp, "#PROMPT SIZE (%d) LONGER THAN ROW SIZE (%d)", len, gtd->screen->cols);
 	}
 
-	if (!HAS_BIT(ses->flags, SES_FLAG_READMUD) && IS_SPLIT(ses))
-	{
-		save_pos(ses);
-	}
+	save_pos(ses);
 
 	if (row == gtd->screen->rows)
 	{
 		gtd->input_off = len + 1;
 
-		printf("\e[%d;1H\e[%d;1H\e[K%s%s\e[%d;%dH\e7\e[%d;1H", row, row, temp, gtd->input_buf, row, gtd->input_off + gtd->input_cur, ses->bot_row);
+		goto_pos(ses, row, col);
+
+		print_stdout("%s%s", temp, gtd->input_buf);
+
+		// bit of a hack
+
+		gtd->screen->sav_col[0] = inputline_cur_pos();
 	}
 	else
 	{
-		printf("\e[%d;%dH\e[%d;%dH%s%s\e[%d;1H", row, col, row, col, clear ? "\e[2K" : "", temp, ses->bot_row);
-	}
-
-	set_line_screen(temp, row - 1, col - 1);
+		goto_pos(ses, row, col);
 
-	if (!HAS_BIT(ses->flags, SES_FLAG_READMUD) && IS_SPLIT(ses))
-	{
-		restore_pos(ses);
+		print_stdout("%s%s", clear ? "\e[2K" : "", temp);
 	}
 
+//	set_line_screen(temp, row - 1, col - 1);
+
+	restore_pos(ses);
 }
+
+

+ 421 - 152
src/substitute.c

@@ -26,6 +26,215 @@
 
 #include "tintin.h"
 
+// color subs
+
+char *c256to16_fg[256] =
+{
+	"\e[22;30m", "\e[22;31m", "\e[22;32m", "\e[22;33m", "\e[22;34m", "\e[22;35m", "\e[22;36m", "\e[22;37m",
+	 "\e[1;30m",  "\e[1;31m",  "\e[1;32m",  "\e[1;33m",  "\e[1;34m",  "\e[1;35m",  "\e[1;36m",  "\e[1;37m",
+
+	"\e[22;30m", "\e[22;34m", "\e[22;34m", "\e[22;34m",  "\e[1;34m",  "\e[1;34m",
+	"\e[22;32m", "\e[22;36m", "\e[22;36m", "\e[22;34m",  "\e[1;34m",  "\e[1;34m",
+	"\e[22;32m", "\e[22;36m", "\e[22;36m", "\e[22;36m",  "\e[1;34m",  "\e[1;34m",
+	"\e[22;32m", "\e[22;32m", "\e[22;36m", "\e[22;36m", "\e[22;36m",  "\e[1;36m",
+	 "\e[1;32m",  "\e[1;32m",  "\e[1;32m", "\e[22;36m",  "\e[1;36m",  "\e[1;36m",
+	 "\e[1;32m",  "\e[1;32m",  "\e[1;32m",  "\e[1;36m",  "\e[1;36m",  "\e[1;36m",
+
+	"\e[22;31m", "\e[22;35m", "\e[22;35m", "\e[22;34m",  "\e[1;34m",  "\e[1;34m",
+	"\e[22;33m",  "\e[1;30m", "\e[22;34m", "\e[22;34m",  "\e[1;34m",  "\e[1;34m",
+	"\e[22;33m", "\e[22;32m", "\e[22;36m", "\e[22;36m",  "\e[1;34m",  "\e[1;34m",
+	"\e[22;32m", "\e[22;32m", "\e[22;36m", "\e[22;36m", "\e[22;36m",  "\e[1;36m",
+	 "\e[1;32m",  "\e[1;32m",  "\e[1;32m", "\e[22;36m",  "\e[1;36m",  "\e[1;36m",
+	 "\e[1;32m",  "\e[1;32m",  "\e[1;32m",  "\e[1;36m",  "\e[1;36m",  "\e[1;36m",
+
+	"\e[22;31m", "\e[22;35m", "\e[22;35m", "\e[22;35m",  "\e[1;34m",  "\e[1;34m",
+	"\e[22;33m", "\e[22;31m", "\e[22;35m", "\e[22;35m",  "\e[1;34m",  "\e[1;34m",
+	"\e[22;33m", "\e[22;33m", "\e[22;37m", "\e[22;34m",  "\e[1;34m",  "\e[1;34m",
+	"\e[22;33m", "\e[22;33m", "\e[22;32m", "\e[22;36m", "\e[22;36m",  "\e[1;34m",
+	 "\e[1;32m",  "\e[1;32m",  "\e[1;32m", "\e[22;36m",  "\e[1;36m",  "\e[1;36m",
+	 "\e[1;32m",  "\e[1;32m",  "\e[1;32m",  "\e[1;32m",  "\e[1;36m",  "\e[1;36m",
+
+	"\e[22;31m", "\e[22;31m", "\e[22;35m", "\e[22;35m", "\e[22;35m",  "\e[1;35m",
+	"\e[22;31m", "\e[22;31m", "\e[22;35m", "\e[22;35m", "\e[22;35m",  "\e[1;35m",
+	"\e[22;33m", "\e[22;33m", "\e[22;31m", "\e[22;35m", "\e[22;35m",  "\e[1;34m",
+	"\e[22;33m", "\e[22;33m", "\e[22;33m", "\e[22;37m",  "\e[1;34m",  "\e[1;34m",
+	"\e[22;33m", "\e[22;33m", "\e[22;33m",  "\e[1;32m",  "\e[1;36m",  "\e[1;36m",
+	 "\e[1;33m",  "\e[1;33m",  "\e[1;32m",  "\e[1;32m",  "\e[1;36m",  "\e[1;36m",
+
+	 "\e[1;31m",  "\e[1;31m",  "\e[1;31m", "\e[22;35m",  "\e[1;35m",  "\e[1;35m",
+	 "\e[1;31m",  "\e[1;31m",  "\e[1;31m", "\e[22;35m",  "\e[1;35m",  "\e[1;35m",
+	 "\e[1;31m",  "\e[1;31m",  "\e[1;31m", "\e[22;35m",  "\e[1;35m",  "\e[1;35m",
+	"\e[22;33m", "\e[22;33m", "\e[22;33m",  "\e[1;31m",  "\e[1;35m",  "\e[1;35m",
+	 "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;37m",  "\e[1;37m",
+	 "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;37m",  "\e[1;37m",
+
+	 "\e[1;31m",  "\e[1;31m",  "\e[1;31m",  "\e[1;35m",  "\e[1;35m",  "\e[1;35m",
+	 "\e[1;31m",  "\e[1;31m",  "\e[1;31m",  "\e[1;35m",  "\e[1;35m",  "\e[1;35m",
+	 "\e[1;31m",  "\e[1;31m",  "\e[1;31m",  "\e[1;31m",  "\e[1;35m",  "\e[1;35m",
+	 "\e[1;33m",  "\e[1;33m",  "\e[1;31m",  "\e[1;31m",  "\e[1;35m",  "\e[1;35m",
+	 "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;37m",  "\e[1;37m",
+	 "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;33m",  "\e[1;37m",  "\e[1;37m",
+
+	 "\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",
+	 "\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",  "\e[1;30m",
+	"\e[22;37m", "\e[22;37m", "\e[22;37m", "\e[22;37m", "\e[22;37m", "\e[22;37m",
+	 "\e[1;37m",  "\e[1;37m",  "\e[1;37m",  "\e[1;37m",  "\e[1;37m",  "\e[1;37m"
+};
+
+char *c256to16_bg[256] =
+{
+	"\e[22;40m", "\e[22;41m", "\e[22;42m", "\e[22;43m", "\e[22;44m", "\e[22;45m", "\e[22;46m", "\e[22;47m",
+	 "\e[1;40m",  "\e[1;41m",  "\e[1;42m",  "\e[1;43m",  "\e[1;44m",  "\e[1;45m",  "\e[1;46m",  "\e[1;47m",
+
+	"\e[22;40m", "\e[22;44m", "\e[22;44m", "\e[22;44m",  "\e[1;44m",  "\e[1;44m",
+	"\e[22;42m", "\e[22;46m", "\e[22;46m", "\e[22;44m",  "\e[1;44m",  "\e[1;44m",
+	"\e[22;42m", "\e[22;46m", "\e[22;46m", "\e[22;46m",  "\e[1;44m",  "\e[1;44m",
+	"\e[22;42m", "\e[22;42m", "\e[22;46m", "\e[22;46m", "\e[22;46m",  "\e[1;46m",
+	 "\e[1;42m",  "\e[1;42m",  "\e[1;42m", "\e[22;46m",  "\e[1;46m",  "\e[1;46m",
+	 "\e[1;42m",  "\e[1;42m",  "\e[1;42m",  "\e[1;46m",  "\e[1;46m",  "\e[1;46m",
+
+	"\e[22;41m", "\e[22;45m", "\e[22;45m", "\e[22;44m",  "\e[1;44m",  "\e[1;44m",
+	"\e[22;43m",  "\e[1;40m", "\e[22;44m", "\e[22;44m",  "\e[1;44m",  "\e[1;44m",
+	"\e[22;43m", "\e[22;42m", "\e[22;46m", "\e[22;46m",  "\e[1;44m",  "\e[1;44m",
+	"\e[22;42m", "\e[22;42m", "\e[22;46m", "\e[22;46m", "\e[22;46m",  "\e[1;46m",
+	 "\e[1;42m",  "\e[1;42m",  "\e[1;42m", "\e[22;46m",  "\e[1;46m",  "\e[1;46m",
+	 "\e[1;42m",  "\e[1;42m",  "\e[1;42m",  "\e[1;46m",  "\e[1;46m",  "\e[1;46m",
+
+	"\e[22;41m", "\e[22;45m", "\e[22;45m", "\e[22;45m",  "\e[1;44m",  "\e[1;44m",
+	"\e[22;43m", "\e[22;41m", "\e[22;45m", "\e[22;45m",  "\e[1;44m",  "\e[1;44m",
+	"\e[22;43m", "\e[22;43m", "\e[22;47m", "\e[22;44m",  "\e[1;44m",  "\e[1;44m",
+	"\e[22;43m", "\e[22;43m", "\e[22;42m", "\e[22;46m", "\e[22;46m",  "\e[1;44m",
+	 "\e[1;42m",  "\e[1;42m",  "\e[1;42m", "\e[22;46m",  "\e[1;46m",  "\e[1;46m",
+	 "\e[1;42m",  "\e[1;42m",  "\e[1;42m",  "\e[1;42m",  "\e[1;46m",  "\e[1;46m",
+
+	"\e[22;41m", "\e[22;41m", "\e[22;45m", "\e[22;45m", "\e[22;45m",  "\e[1;45m",
+	"\e[22;41m", "\e[22;41m", "\e[22;45m", "\e[22;45m", "\e[22;45m",  "\e[1;45m",
+	"\e[22;43m", "\e[22;43m", "\e[22;41m", "\e[22;45m", "\e[22;45m",  "\e[1;44m",
+	"\e[22;43m", "\e[22;43m", "\e[22;43m", "\e[22;47m",  "\e[1;44m",  "\e[1;44m",
+	"\e[22;43m", "\e[22;43m", "\e[22;43m",  "\e[1;42m",  "\e[1;46m",  "\e[1;46m",
+	 "\e[1;43m",  "\e[1;43m",  "\e[1;42m",  "\e[1;42m",  "\e[1;46m",  "\e[1;46m",
+
+	 "\e[1;41m",  "\e[1;41m",  "\e[1;41m", "\e[22;45m",  "\e[1;45m",  "\e[1;45m",
+	 "\e[1;41m",  "\e[1;41m",  "\e[1;41m", "\e[22;45m",  "\e[1;45m",  "\e[1;45m",
+	 "\e[1;41m",  "\e[1;41m",  "\e[1;41m", "\e[22;45m",  "\e[1;45m",  "\e[1;45m",
+	"\e[22;43m", "\e[22;43m", "\e[22;43m",  "\e[1;41m",  "\e[1;45m",  "\e[1;45m",
+	 "\e[1;43m",  "\e[1;43m",  "\e[1;43m",  "\e[1;43m",  "\e[1;47m",  "\e[1;47m",
+	 "\e[1;43m",  "\e[1;43m",  "\e[1;43m",  "\e[1;43m",  "\e[1;47m",  "\e[1;47m",
+
+	 "\e[1;41m",  "\e[1;41m",  "\e[1;41m",  "\e[1;45m",  "\e[1;45m",  "\e[1;45m",
+	 "\e[1;41m",  "\e[1;41m",  "\e[1;41m",  "\e[1;45m",  "\e[1;45m",  "\e[1;45m",
+	 "\e[1;41m",  "\e[1;41m",  "\e[1;41m",  "\e[1;41m",  "\e[1;45m",  "\e[1;45m",
+	 "\e[1;43m",  "\e[1;43m",  "\e[1;41m",  "\e[1;41m",  "\e[1;45m",  "\e[1;45m",
+	 "\e[1;43m",  "\e[1;43m",  "\e[1;43m",  "\e[1;43m",  "\e[1;47m",  "\e[1;47m",
+	 "\e[1;43m",  "\e[1;43m",  "\e[1;43m",  "\e[1;43m",  "\e[1;47m",  "\e[1;47m",
+
+	 "\e[1;40m",  "\e[1;40m",  "\e[1;40m",  "\e[1;40m",  "\e[1;40m",  "\e[1;40m",
+	 "\e[1;40m",  "\e[1;40m",  "\e[1;40m",  "\e[1;40m",  "\e[1;40m",  "\e[1;40m",
+	"\e[22;47m", "\e[22;47m", "\e[22;47m", "\e[22;47m", "\e[22;47m", "\e[22;47m",
+	 "\e[1;47m",  "\e[1;47m",  "\e[1;47m",  "\e[1;47m",  "\e[1;47m",  "\e[1;47m"
+};
+
+// input 00 to FF
+
+int c4096_val(char chr1, char chr2)
+{
+	static unsigned char c4096_val[256] =
+	{
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   1,   2,   3,    4,   5,   6,   7,   8,   9,    0,   0,   0,   0,   0,   0,
+		  0,  10,  11,  12,   13,  14,  15,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,  10,  11,  12,   13,  14,  15,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0
+	};
+
+	return (int) c4096_val[(unsigned char) chr1] * 16 + c4096_val[(unsigned char) chr2];
+}
+
+// input 00 to FF
+
+int c4096_to_256_val(char chr1, char chr2)
+{
+	static unsigned char c4096_to_256[256] =
+	{
+		  0,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
+		  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
+		  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
+		  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
+		  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
+		  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
+		  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
+		  2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+		  2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+		  3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
+		  3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
+		  3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
+		  4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+		  4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+		  4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+		  5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+	};
+
+	return (int) c4096_to_256[c4096_val(chr1, chr2)];
+}
+
+
+void c4096_rnd(struct session *ses, char *str)
+{
+	static char dec_to_hex[16] =
+	{
+		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+	};
+	sprintf(str, "%c%c%c", dec_to_hex[generate_rand(ses) % 16], dec_to_hex[generate_rand(ses) % 16], dec_to_hex[generate_rand(ses) % 16]);
+}
+
+int is_c32(char chr)
+{
+	static unsigned char c32_lookup[256] =
+	{
+		  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  '?',
+		  0,  'A',  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
+		  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,  'Z',  0,   0,   0,   0,   0,
+		  0,  'a',  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
+		  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,  'z',  0,   0,   0,   0,   0,
+		  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
+	};
+
+	return (int) c32_lookup[(unsigned char) chr];
+}
+
+char *c32_fg_dark[26] =
+{
+	"<f06b>", "<f00b>", "<f0bb>", "", "<f000>", "", "<f0b0>", "", "", "<f0b6>", "", "<f6b0>", "<fb0b>",
+	"", "<fb60>", "<fb06>", "", "<fb00>", "<f888>", "<f860>", "", "<f60b>", "<fbbb>", "", "<fbb0>", ""
+};
+
+char *c32_fg_bold[26] =
+{
+	"<f08f>", "<f00f>", "<f0ff>", "", "<f666>", "", "<f0f0>", "", "", "<f0f8>", "", "<f8f0>", "<ff0f>",
+	"", "<ff80>", "<ff08>", "", "<ff00>", "<fddd>", "<fdb0>", "", "<f80f>", "<ffff>", "", "<fff0>", ""
+};
+
+
 int is_variable(struct session *ses, char *str)
 {
 	struct listroot *root;
@@ -113,6 +322,85 @@ int is_function(struct session *ses, char *str)
 	return TRUE;
 }
 
+int is_color_code(char *pti)
+{
+	if (pti[0] == '<')
+	{
+		if (pti[1] == 0 || pti[2] == 0 || pti[3] == 0 || pti[4] == 0)
+		{
+			return 0;
+		}
+
+		if (pti[4] == '>')
+		{
+
+			if (isdigit(pti[1]) && isdigit(pti[2]) && isdigit(pti[3]))
+			{
+				return 5;
+			}
+			if (pti[1] >= 'a' && pti[1] <= 'f' && pti[2] >= 'a' && pti[2] <= 'f' && pti[3] >= 'a' && pti[3] <= 'f')
+			{
+				return 5;
+			}
+			if (pti[1] >= 'A' && pti[1] <= 'F' && pti[2] >= 'A' && pti[2] <= 'F' && pti[3] >= 'A' && pti[3] <= 'F')
+			{
+				return 5;
+			}
+
+			if (pti[1] == 'g' || pti[1] == 'G')
+			{
+				if (isdigit((int) pti[2]) && isdigit((int) pti[3]))
+				{
+					return 5;
+				}
+				return 0;
+			}
+
+			return 0;
+		}
+
+		if (pti[5] == 0)
+		{
+			return 0;
+		}
+
+		if (toupper((int) pti[1]) == 'F')
+		{
+			if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && pti[5] == '>')
+			{
+				return 6;
+			}
+			else if (pti[2] == '?' && pti[3] == '?' && pti[4] == '?' && pti[5] == '>')
+			{
+				return 6;
+			}
+			else if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && isxdigit(pti[5]) && isxdigit(pti[6]) && isxdigit(pti[7]) && pti[8] == '>')
+			{
+				return 9;
+			}
+			return 0;
+		}
+
+		if (toupper(pti[1]) == 'B')
+		{
+			if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && pti[5] == '>')
+			{
+				return 6;
+			}
+			if (toupper(pti[1]) == 'B' && pti[2] == '?' && pti[3] == '?' && pti[4] == '?' && pti[5] == '>')
+			{
+				return 6;
+			}
+			if (toupper(pti[1]) == 'B' && isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && isxdigit(pti[5]) && isxdigit(pti[6]) && isxdigit(pti[7]) && pti[8] == '>')
+			{
+				return 9;
+			}
+			return 0;
+		}
+	}
+	return 0;
+}
+
 int substitute(struct session *ses, char *string, char *result, int flags)
 {
 	struct listnode *node;
@@ -131,7 +419,7 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 
 	while (TRUE)
 	{
-		if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -279,6 +567,11 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 
 					substitute(ses, node->arg2, buf, SUB_ARG);
 
+					if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+					{
+						delete_node_list(ses, LIST_FUNCTION, node);
+					}
+
 					script_driver(ses, LIST_FUNCTION, buf);
 
 					substitute(ses, "$result", pto, flags_neol|SUB_VAR);
@@ -635,7 +928,7 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 
 						while (*ptt)
 						{
-							if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *ptt & 128 && ptt[1] != 0)
+							if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *ptt & 128 && ptt[1] != 0)
 							{
 								*pto++ = *ptt++;
 								*pto++ = *ptt++;
@@ -717,13 +1010,13 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 				break;
 
 			case '<':
-				if (HAS_BIT(flags, SUB_COL) && isalnum((int) pti[1]))
+				if (HAS_BIT(flags, SUB_COL))
 				{
 					if (HAS_BIT(flags, SUB_CMP) && old[0] && !strncmp(old, pti, strlen(old)))
 					{
 						pti += strlen(old);
 					}
-					else if (isdigit((int) pti[1]) && isdigit((int) pti[2]) && isdigit((int) pti[3]) && pti[4] == '>')
+					else if (isdigit(pti[1]) && isdigit(pti[2]) && isdigit(pti[3]) && pti[4] == '>')
 					{
 						if (pti[1] != '8' || pti[2] != '8' || pti[3] != '8')
 						{
@@ -770,182 +1063,158 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 					}
 					else if (pti[1] >= 'a' && pti[1] <= 'f' && pti[2] >= 'a' && pti[2] <= 'f' && pti[3] >= 'a' && pti[3] <= 'f' && pti[4] == '>')
 					{
-						*pto++ = ASCII_ESC;
-						*pto++ = '[';
-						*pto++ = '3';
-						*pto++ = '8';
-						*pto++ = ';';
-						*pto++ = '5';
-						*pto++ = ';';
 						cnt = 16 + (pti[1] - 'a') * 36 + (pti[2] - 'a') * 6 + (pti[3] - 'a');
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = 'm';
+
+						if (ses->color >= 256)
+						{
+							pto += sprintf(pto, "\e[38;5;%dm", cnt);
+						}
+						else if (ses->color == 16)
+						{
+							pto += sprintf(pto, "%s", c256to16_fg[cnt]);
+						}
 						pti += sprintf(old, "<%c%c%c>", pti[1], pti[2], pti[3]);
 					}
 					else if (pti[1] >= 'A' && pti[1] <= 'F' && pti[2] >= 'A' && pti[2] <= 'F' && pti[3] >= 'A' && pti[3] <= 'F' && pti[4] == '>')
 					{
-						*pto++ = ASCII_ESC;
-						*pto++ = '[';
-						*pto++ = '4';
-						*pto++ = '8';
-						*pto++ = ';';
-						*pto++ = '5';
-						*pto++ = ';';
 						cnt = 16 + (pti[1] - 'A') * 36 + (pti[2] - 'A') * 6 + (pti[3] - 'A');
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = 'm';
+
+						if (ses->color >= 256)
+						{
+							pto += sprintf(pto, "\e[48;5;%dm", cnt);
+						}
+						else if (ses->color == 16)
+						{
+							pto += sprintf(pto, "%s", c256to16_bg[cnt]);
+						}
 						pti += sprintf(old, "<%c%c%c>", pti[1], pti[2], pti[3]);
 					}
 					else if (pti[1] == 'g' && isdigit((int) pti[2]) && isdigit((int) pti[3]) && pti[4] == '>')
 					{
-						*pto++ = ASCII_ESC;
-						*pto++ = '[';
-						*pto++ = '3';
-						*pto++ = '8';
-						*pto++ = ';';
-						*pto++ = '5';
-						*pto++ = ';';
 						cnt = 232 + (pti[2] - '0') * 10 + (pti[3] - '0');
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = 'm';
+
+						if (ses->color >= 256)
+						{
+							pto += sprintf(pto, "\e[38;5;%dm", cnt);
+						}
+						else if (ses->color == 16)
+						{
+							pto += sprintf(pto, "%s", c256to16_fg[cnt]);
+						}
 						pti += sprintf(old, "<g%c%c>", pti[2], pti[3]);
 					}
 					else if (pti[1] == 'G' && isdigit((int) pti[2]) && isdigit((int) pti[3]) && pti[4] == '>')
 					{
-						*pto++ = ASCII_ESC;
-						*pto++ = '[';
-						*pto++ = '4';
-						*pto++ = '8';
-						*pto++ = ';';
-						*pto++ = '5';
-						*pto++ = ';';
 						cnt = 232 + (pti[2] - '0') * 10 + (pti[3] - '0');
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = 'm';
+
+						if (ses->color >= 256)
+						{
+							pto += sprintf(pto, "\e[48;5;%dm", cnt);
+						}
+						else if (ses->color == 16)
+						{
+							pto += sprintf(pto, "%s", c256to16_bg[cnt]);
+						}
 						pti += sprintf(old, "<G%c%c>", pti[2], pti[3]);
 					}
-					else if (toupper((int) pti[1]) == 'F' && isxdigit((int) pti[2]) && isxdigit((int) pti[3]) && isxdigit((int) pti[4]) && pti[5] == '>')
+					else if (toupper((int) pti[1]) == 'F' && isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && pti[5] == '>')
 					{
-						*pto++ = ASCII_ESC;
-						*pto++ = '[';
-						*pto++ = '3';
-						*pto++ = '8';
-						*pto++ = ';';
-						*pto++ = '2';
-						*pto++ = ';';
-						cnt  = isdigit(pti[2]) ? (pti[2] - '0') : (pti[2] - 'A' + 10);
-						cnt += cnt * 16;
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = ';';
-						cnt  = isdigit(pti[3]) ? (pti[3] - '0') : (pti[3] - 'A' + 10);
-						cnt += cnt * 16;
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = ';';
-						cnt  = isdigit(pti[4]) ? (pti[4] - '0') : (pti[4] - 'A' + 10);
-						cnt += cnt * 16;
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = 'm';
+						if (ses->color == 4096)
+						{
+							pto += sprintf(pto, "\e[38;2;%d;%d;%dm", c4096_val(pti[2], pti[2]), c4096_val(pti[3], pti[3]), c4096_val(pti[4], pti[4]));
+						}
+						else if (ses->color == 256)
+						{
+							pto += sprintf(pto, "\033[38;5;%dm",  16 + c4096_to_256_val(pti[2], pti[2]) * 36 + c4096_to_256_val(pti[3], pti[3]) * 6 + c4096_to_256_val(pti[4], pti[4]));
+						}
+						else if (ses->color == 16)
+						{
+							pto += sprintf(pto, "%s", c256to16_fg[16 + c4096_to_256_val(pti[2], pti[2]) * 36 + c4096_to_256_val(pti[3], pti[3]) * 6 + c4096_to_256_val(pti[4], pti[4])]);
+						}
+						pti += sprintf(old, "<F%c%c%c>", pti[2], pti[3], pti[4]);
+					}
+					else if (toupper(pti[1]) == 'F' && pti[2] == '?' && pti[3] == '?' && pti[4] == '?' && pti[5] == '>')
+					{
+						c4096_rnd(ses, &pti[2]);
+
+						if (ses->color == 4096)
+						{
+							pto += sprintf(pto, "\e[38;2;%d;%d;%dm", c4096_val(pti[2], pti[2]), c4096_val(pti[3], pti[3]), c4096_val(pti[4], pti[4]));
+						}
+						else if (ses->color == 256)
+						{
+							pto += sprintf(pto, "\033[38;5;%dm",  16 + c4096_to_256_val(pti[2], pti[2]) * 36 + c4096_to_256_val(pti[3], pti[3]) * 6 + c4096_to_256_val(pti[4], pti[4]));
+						}
+						else if (ses->color == 16)
+						{
+							pto += sprintf(pto, "%s", c256to16_fg[16 + c4096_to_256_val(pti[2], pti[2]) * 36 + c4096_to_256_val(pti[3], pti[3]) * 6 + c4096_to_256_val(pti[4], pti[4])]);
+						}
 						pti += sprintf(old, "<F%c%c%c>", pti[2], pti[3], pti[4]);
 					}
-					else if (toupper((int) pti[1]) == 'F' && isxdigit((int) pti[2]) && isxdigit((int) pti[3]) && isxdigit((int) pti[4]) && isxdigit((int) pti[5]) && isxdigit((int) pti[6]) && isxdigit((int) pti[7]) && pti[8] == '>')
+					else if (toupper((int) pti[1]) == 'F' && isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && isxdigit(pti[5]) && isxdigit(pti[6]) && isxdigit(pti[7]) && pti[8] == '>')
 					{
-						*pto++ = ASCII_ESC;
-						*pto++ = '[';
-						*pto++ = '3';
-						*pto++ = '8';
-						*pto++ = ';';
-						*pto++ = '2';
-						*pto++ = ';';
-						cnt  = isdigit(pti[2]) ? 16 * (pti[2] - '0') : 16 * (pti[2] - 'A' + 10);
-						cnt += isdigit(pti[3]) ?  1 * (pti[3] - '0') :  1 * (pti[3] - 'A' + 10);
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = ';';
-						cnt  = isdigit(pti[4]) ? 16 * (pti[4] - '0') : 16 * (pti[4] - 'A' + 10);
-						cnt += isdigit(pti[5]) ?  1 * (pti[5] - '0') :  1 * (pti[5] - 'A' + 10);
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = ';';
-						cnt  = isdigit(pti[6]) ? 16 * (pti[6] - '0') : 16 * (pti[6] - 'A' + 10);
-						cnt += isdigit(pti[7]) ?  1 * (pti[7] - '0') :  1 * (pti[7] - 'A' + 10);
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = 'm';
+						if (ses->color == 4096)
+						{
+							pto += sprintf(pto, "\e[38;2;%d;%d;%dm", c4096_val(pti[2], pti[3]), c4096_val(pti[4], pti[5]), c4096_val(pti[6], pti[7]));
+						}
+						else if (ses->color == 256)
+						{
+							pto += sprintf(pto, "\033[38;5;%dm",  16 + c4096_to_256_val(pti[2], pti[3]) * 36 + c4096_to_256_val(pti[4], pti[5]) * 6 + c4096_to_256_val(pti[6], pti[7]));
+						}
+						else if (ses->color == 16)
+						{
+							pto += sprintf(pto, "%s", c256to16_fg[16 + c4096_to_256_val(pti[2], pti[3]) * 36 + c4096_to_256_val(pti[4], pti[5]) * 6 + c4096_to_256_val(pti[6], pti[7])]);
+						}
 						pti += sprintf(old, "<F%c%c%c%c%c%c>", pti[2], pti[3], pti[4], pti[5], pti[6], pti[7]);
 					}
-					else if (toupper((int) pti[1]) == 'B' && isxdigit((int) pti[2]) && isxdigit((int) pti[3]) && isxdigit((int) pti[4]) && pti[5] == '>')
+					else if (toupper(pti[1]) == 'B' && isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && pti[5] == '>')
 					{
-						*pto++ = ASCII_ESC;
-						*pto++ = '[';
-						*pto++ = '4';
-						*pto++ = '8';
-						*pto++ = ';';
-						*pto++ = '2';
-						*pto++ = ';';
-						cnt  = isdigit(pti[2]) ? (pti[2] - '0') : (pti[2] - 'A' + 10);
-						cnt += cnt * 16;
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = ';';
-						cnt  = isdigit(pti[3]) ? (pti[3] - '0') : (pti[3] - 'A' + 10);
-						cnt += cnt * 16;
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = ';';
-						cnt  = isdigit(pti[4]) ? (pti[4] - '0') : (pti[4] - 'A' + 10);
-						cnt += cnt * 16;
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = 'm';
+						if (ses->color == 4096)
+						{
+							pto += sprintf(pto, "\e[48;2;%d;%d;%dm", c4096_val(pti[2], pti[2]), c4096_val(pti[3], pti[3]), c4096_val(pti[4], pti[4]));
+						}
+						else if (ses->color == 256)
+						{
+							pto += sprintf(pto, "\033[48;5;%dm",  16 + c4096_to_256_val(pti[2], pti[2]) * 36 + c4096_to_256_val(pti[3], pti[3]) * 6 + c4096_to_256_val(pti[4], pti[4]));
+						}
+						else if (ses->color == 16)
+						{
+							pto += sprintf(pto, "%s", c256to16_bg[16 + c4096_to_256_val(pti[2], pti[2]) * 36 + c4096_to_256_val(pti[3], pti[3]) * 6 + c4096_to_256_val(pti[4], pti[4])]);
+						}
 						pti += sprintf(old, "<B%c%c%c>", pti[2], pti[3], pti[4]);
 					}
-					else if (toupper((int) pti[1]) == 'B' && isxdigit((int) pti[2]) && isxdigit((int) pti[3]) && isxdigit((int) pti[4]) && isxdigit((int) pti[5]) && isxdigit((int) pti[6]) && isxdigit((int) pti[7]) && pti[8] == '>')
+					else if (toupper(pti[1]) == 'B' && pti[2] == '?' && pti[3] == '?' && pti[4] == '?' && pti[5] == '>')
 					{
-						*pto++ = ASCII_ESC;
-						*pto++ = '[';
-						*pto++ = '4';
-						*pto++ = '8';
-						*pto++ = ';';
-						*pto++ = '2';
-						*pto++ = ';';
-						cnt  = isdigit(pti[2]) ? 16 * (pti[2] - '0') : 16 * (pti[2] - 'A' + 10);
-						cnt += isdigit(pti[3]) ?  1 * (pti[3] - '0') :  1 * (pti[3] - 'A' + 10);
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = ';';
-						cnt  = isdigit(pti[4]) ? 16 * (pti[4] - '0') : 16 * (pti[4] - 'A' + 10);
-						cnt += isdigit(pti[5]) ?  1 * (pti[5] - '0') :  1 * (pti[5] - 'A' + 10);
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = ';';
-						cnt  = isdigit(pti[6]) ? 16 * (pti[6] - '0') : 16 * (pti[6] - 'A' + 10);
-						cnt += isdigit(pti[7]) ?  1 * (pti[7] - '0') :  1 * (pti[7] - 'A' + 10);
-						*pto++ = '0' + cnt / 100;
-						*pto++ = '0' + cnt % 100 / 10;
-						*pto++ = '0' + cnt % 10;
-						*pto++ = 'm';
+						c4096_rnd(ses, &pti[2]);
+
+						if (ses->color == 4096)
+						{
+							pto += sprintf(pto, "\e[48;2;%d;%d;%dm", c4096_val(pti[2], pti[2]), c4096_val(pti[3], pti[3]), c4096_val(pti[4], pti[4]));
+						}
+						else if (ses->color == 256)
+						{
+							pto += sprintf(pto, "\033[48;5;%dm",  16 + c4096_to_256_val(pti[2], pti[2]) * 36 + c4096_to_256_val(pti[3], pti[3]) * 6 + c4096_to_256_val(pti[4], pti[4]));
+						}
+						else if (ses->color == 16)
+						{
+							pto += sprintf(pto, "%s", c256to16_bg[16 + c4096_to_256_val(pti[2], pti[2]) * 36 + c4096_to_256_val(pti[3], pti[3]) * 6 + c4096_to_256_val(pti[4], pti[4])]);
+						}
+						pti += sprintf(old, "<F%c%c%c>", pti[2], pti[3], pti[4]);
+					}
+					else if (toupper(pti[1]) == 'B' && isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && isxdigit(pti[5]) && isxdigit(pti[6]) && isxdigit(pti[7]) && pti[8] == '>')
+					{
+						if (ses->color == 4096)
+						{
+							pto += sprintf(pto, "\e[48;2;%d;%d;%dm", c4096_val(pti[2], pti[3]), c4096_val(pti[4], pti[5]), c4096_val(pti[6], pti[7]));
+						}
+						else if (ses->color == 256)
+						{
+							pto += sprintf(pto, "\033[48;5;%dm",  16 + c4096_to_256_val(pti[2], pti[3]) * 36 + c4096_to_256_val(pti[4], pti[5]) * 6 + c4096_to_256_val(pti[6], pti[7]));
+						}
+						else if (ses->color == 16)
+						{
+							pto += sprintf(pto, "%s", c256to16_bg[16 + c4096_to_256_val(pti[2], pti[3]) * 36 + c4096_to_256_val(pti[4], pti[5]) * 6 + c4096_to_256_val(pti[6], pti[7])]);
+						}
 						pti += sprintf(old, "<B%c%c%c%c%c%c>", pti[2], pti[3], pti[4], pti[5], pti[6], pti[7]);
 					}
 					else

+ 25 - 13
src/system.c

@@ -33,13 +33,16 @@
 #include <util.h>
 #endif
 #endif
+#include <fcntl.h>  
+#include <dirent.h>
+#include <termios.h>
+#include <sys/un.h>
 
 DO_COMMAND(do_run)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], temp[BUFFER_SIZE], file[BUFFER_SIZE];
 	int desc, pid;
 	struct winsize size;
-	struct termios run_terminal;
 
 	char *argv[4] = {"sh", "-c", "", NULL};
 
@@ -54,8 +57,10 @@ DO_COMMAND(do_run)
 		return ses;
 	}
 
-	size.ws_row = get_scroll_size(ses);
-	size.ws_col = gtd->screen->cols;
+	size.ws_row = get_scroll_rows(ses);
+	size.ws_col = get_scroll_cols(ses);
+	size.ws_ypixel = size.ws_row * gtd->screen->char_height;
+	size.ws_xpixel = size.ws_col * gtd->screen->char_width;
 
 	pid = forkpty(&desc, temp, &gtd->old_terminal, &size);
 
@@ -69,22 +74,17 @@ DO_COMMAND(do_run)
 			sprintf(temp, "exec %s", arg2);
 			argv[2] = temp;
 			execv("/bin/sh", argv);
-			tcgetattr(0, &run_terminal);
 			break;
 
 		default:
 			sprintf(temp, "{%s} {%d} {%s}", arg2, pid, file);
-
 			ses = new_session(ses, arg1, temp, desc, 0);
-
-//			memcpy(&ses->cur_terminal, &run_terminal, sizeof(run_terminal));
-
-//			refresh_session_terminal(ses);
 			break;
 	}
 	return ses;
 }
 
+
 DO_COMMAND(do_script)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], *cptr, buf[BUFFER_SIZE], var[BUFFER_SIZE], tmp[BUFFER_SIZE];
@@ -164,12 +164,25 @@ DO_COMMAND(do_script)
 
 DO_COMMAND(do_suspend)
 {
-	suspend_handler(0);
+	print_stdout("\e[r\e[%d;%dH", gtd->screen->rows, 1);
+
+	fflush(NULL);
+
+	reset_terminal(gtd->ses);
+
+	kill(0, SIGSTOP);
+
+	init_terminal(gtd->ses);
+
+	dirty_screen(gtd->ses);
+
+	tintin_puts(NULL, "#RETURNING BACK TO TINTIN++.");
 
 	return ses;
 }
 
 
+
 DO_COMMAND(do_system)
 {
 	char arg1[BUFFER_SIZE];
@@ -188,9 +201,9 @@ DO_COMMAND(do_system)
 	if (!HAS_BIT(gtd->ses->flags, SES_FLAG_READMUD) && IS_SPLIT(gtd->ses))
 	{
 		save_pos(gtd->ses);
-		goto_rowcol(gtd->ses, gtd->ses->bot_row, 1);
+
+		goto_pos(gtd->ses, gtd->ses->split->bot_row, 1);
 	}
-	fflush(stdout);
 
 	system(arg1);
 
@@ -198,7 +211,6 @@ DO_COMMAND(do_system)
 	{
 		restore_pos(gtd->ses);
 	}
-	fflush(stdout);
 
 	refresh_session_terminal(gtd->ses);
 

+ 365 - 317
src/tables.c

@@ -34,6 +34,7 @@ struct command_type command_table[] =
 	{    "advertise",         do_advertise,         TOKEN_TYPE_COMMAND },
 	{    "alias",             do_alias,             TOKEN_TYPE_COMMAND },
 	{    "all",               do_all,               TOKEN_TYPE_COMMAND },
+//	{    "attach",            do_attach,            TOKEN_TYPE_COMMAND },
 	{    "bell",              do_bell,              TOKEN_TYPE_COMMAND },
 	{    "break",             do_nop,               TOKEN_TYPE_BREAK   },
 	{    "buffer",            do_buffer,            TOKEN_TYPE_COMMAND },
@@ -46,16 +47,17 @@ struct command_type command_table[] =
 	{    "continue",          do_nop,               TOKEN_TYPE_CONTINUE},
 	{    "cr",                do_cr,                TOKEN_TYPE_COMMAND },
 	{    "cursor",            do_cursor,            TOKEN_TYPE_COMMAND },
+	{    "daemon",            do_daemon,            TOKEN_TYPE_COMMAND },
 	{    "debug",             do_debug,             TOKEN_TYPE_COMMAND },
 	{    "default",           do_nop,               TOKEN_TYPE_DEFAULT },
 	{    "delay",             do_delay,             TOKEN_TYPE_COMMAND },
+//	{    "detach",            do_detach,            TOKEN_TYPE_COMMAND },
 	{    "draw",              do_draw,              TOKEN_TYPE_COMMAND },
 	{    "echo",              do_echo,              TOKEN_TYPE_COMMAND },
 	{    "else",              do_nop,               TOKEN_TYPE_ELSE    },
 	{    "elseif",            do_nop,               TOKEN_TYPE_ELSEIF  },
 	{    "end",               do_end,               TOKEN_TYPE_COMMAND },
 	{    "event",             do_event,             TOKEN_TYPE_COMMAND },
-	{    "forall",            do_forall,            TOKEN_TYPE_COMMAND },
 	{    "foreach",           do_nop,               TOKEN_TYPE_FOREACH },
 	{    "format",            do_format,            TOKEN_TYPE_COMMAND },
 	{    "function",          do_function,          TOKEN_TYPE_COMMAND },
@@ -67,7 +69,8 @@ struct command_type command_table[] =
 	{    "if",                do_nop,               TOKEN_TYPE_IF      },
 	{    "ignore",            do_ignore,            TOKEN_TYPE_COMMAND },
 	{    "info",              do_info,              TOKEN_TYPE_COMMAND },
-	{    "killall",           do_kill,              TOKEN_TYPE_COMMAND },
+	{    "kill",              do_kill,              TOKEN_TYPE_COMMAND },
+	{    "killall",           do_killall,           TOKEN_TYPE_COMMAND },
 	{    "line",              do_line,              TOKEN_TYPE_COMMAND },
 	{    "list",              do_list,              TOKEN_TYPE_COMMAND },
 	{    "local",             do_local,             TOKEN_TYPE_COMMAND },
@@ -173,21 +176,21 @@ struct config_type config_table[] =
 	{
 		"AUTO TAB",
 		"",
-		"Scroll back buffer lines used for tab completion",
+		"Buffer lines used for tab completion",
 		config_autotab
 	},
 
 	{
 		"BUFFER SIZE",
 		"",
-		"The size of the scroll back buffer",
+		"The size of the scrollback buffer",
 		config_buffersize
 	},
 
 	{
 		"CHARSET",
 		"",
-		"The character set encoding used by TinTin++",
+		"The character set encoding used",
 		config_charset
 	},
 
@@ -201,14 +204,14 @@ struct config_type config_table[] =
 	{
 		"COLOR MODE",
 		"",
-		"The color code encoding used by TinTin++",
+		"The color code encoding used",
 		config_colormode
 	},
 
 	{
 		"COLOR PATCH",
-		"TinTin++ will properly color the start of each line",
-		"TinTin++ will leave color handling to the server",
+		"Color the start of each line",
+		"Leave color handling up to the server",
 		config_colorpatch
 	},
 
@@ -221,22 +224,22 @@ struct config_type config_table[] =
 
 	{
 		"COMMAND ECHO",
-		"Your commands are echoed in split mode",
-		"Your commands are not echoed in split mode",
+		"Commands are echoed in split mode",
+		"Commands are not echoed in split mode",
 		config_commandecho
 	},
 
 	{
 		"CONNECT RETRY",
 		"",
-		"Seconds TinTin++ sessions try to connect on failure",
+		"Seconds sessions try to connect on failure",
 		config_connectretry
 	},
 
 	{
 		"CONVERT META",
-		"TinTin++ converts meta prefixed characters",
-		"TinTin++ doesn't convert meta prefixed characters",
+		"TinTin++ converts meta characters",
+		"TinTin++ doesn't convert meta characters",
 		config_convertmeta
 	},
 
@@ -284,39 +287,46 @@ struct config_type config_table[] =
 
 	{
 		"MOUSE TRACKING",
-		"Your terminal generates mouse events.",
-		"Your terminal does not generate mouse events.",
+		"Generates mouse tracking events.",
+		"Do does not generate mouse events.",
 		config_mousetracking
 	},
 
 	{
 		"PACKET PATCH",
 		"",
-		"Seconds TinTin++ will try to patch broken packets",
+		"Seconds to try to patch broken packets",
 		config_packetpatch
 	},
 
 	{
-		"RANDOM SEED",
+		"PID",
 		"",
-		"Seed value used for the random number engine",
-		config_randomseed
+		"The PID of the master process.",
+		config_pid
 	},
 
 	{
-		"REPEAT ENTER",
-		"You send the last command on an enter",
-		"You send a carriage return on an enter",
-		config_repeatenter
+		"RANDOM SEED",
+		"",
+		"Seed value used for random numbers",
+		config_randomseed
 	},
 
 	{
 		"REPEAT CHAR",
 		"",
-		"The character used for repeating commands",
+		"Character used for repeating commands",
 		config_repeatchar
 	},
 
+	{
+		"REPEAT ENTER",
+		"You send the last command on an enter",
+		"You send a carriage return on an enter",
+		config_repeatenter
+	},
+
 	{
 		"SCREEN READER",
 		"You are using a screen reader",
@@ -333,11 +343,18 @@ struct config_type config_table[] =
 
 	{
 		"SPEEDWALK",
-		"Your input is scanned for speedwalk directions",
-		"Your input is not scanned for speedwalk directions",
+		"Your input is scanned for speedwalks",
+		"Your input is not scanned for speedwalks",
 		config_speedwalk
 	},
 
+	{
+		"TAB WIDTH",
+		"",
+		"Number of spaces used for a tab",
+		config_tabwidth
+	},
+
 	{
 		"TELNET",
 		"TELNET support is enabled.",
@@ -348,28 +365,28 @@ struct config_type config_table[] =
 	{
 		"TINTIN CHAR",
 		"",
-		"The character used for TinTin++ commands",
+		"Character used for TinTin++ commands",
 		config_tintinchar
 	},
 
 	{
 		"VERBATIM",
-		"Your keyboard input isn't modified by TinTin++",
-		"Your keyboard input is parsed by TinTin++",
+		"Keyboard input is send as is",
+		"Keyboard input is parsed by TinTin++",
 		config_verbatim
 	},
 
 	{
 		"VERBATIM CHAR",
 		"",
-		"The character used for unparsed text",
+		"Character used for verbatim lines",
 		config_verbatimchar
 	},
 
 	{
 		"VERBOSE",
-		"Messages while reading in a script file are echoed",
-		"Messages while reading in a script file are gagged",
+		"Read script files verbosely",
+		"Read script files quietly",
 		config_verbose
 	},
 
@@ -391,69 +408,123 @@ struct config_type config_table[] =
 
 struct color_type color_table[] =
 {
-	{    "azure",         "<abd>" },
-	{    "ebony",         "<g04>" },
-	{    "jade",          "<adb>" },
-	{    "lime",          "<bda>" },
-	{    "orange",        "<dba>" },
-	{    "pink",          "<dab>" },
-	{    "silver",        "<ccc>" },
-	{    "tan",           "<cba>" },
-	{    "violet",        "<bad>" },
-
-	{    "light azure",   "<acf>" },
-	{    "light ebony",   "<bbb>" },
-	{    "light jade",    "<afc>" },
-	{    "light lime",    "<cfa>" },
-	{    "light orange",  "<fca>" },
-	{    "light pink",    "<fac>" },
-	{    "light silver",  "<eee>" },
-	{    "light tan",     "<eda>" },
-	{    "light violet",  "<caf>" },
-
-	{    "reset",         "<088>" },
-	{    "light",         "<188>" },
-	{    "bold",          "<188>" },
-	{    "faint",         "<288>" },
-	{    "dim",           "<288>" },
-	{    "dark",          "<288>" },
-	{    "underscore",    "<488>" },
-	{    "blink",         "<588>" },
-	{    "reverse",       "<788>" },
-
-	{    "no-underscore", "\e[24m"},
-	{    "no-blink",      "\e[25m"},
-	{    "no-reverse",    "\e[27m"},
-		
-	{    "black",         "<808>" },
-	{    "red",           "<818>" },
-	{    "green",         "<828>" },
-	{    "yellow",        "<838>" },
-	{    "blue",          "<848>" },
-	{    "magenta",       "<858>" },
-	{    "cyan",          "<868>" },
-	{    "white",         "<878>" },
-
-	{    "b black",       "<880>" },
-	{    "b red",         "<881>" },
-	{    "b green",       "<882>" },
-	{    "b yellow",      "<883>" },
-	{    "b blue",        "<884>" },
-	{    "b magenta",     "<885>" },
-	{    "b cyan",        "<886>" },
-	{    "b white",       "<887>" },
-
-	{    "b azure",       "<ABD>" },
-	{    "b ebony",       "<G04>" },
-	{    "b jade",        "<ADB>" },
-	{    "b lime",        "<BDA>" },
-	{    "b orange",      "<DBA>" },
-	{    "b pink",        "<DAB>" },
-	{    "b silver",      "<CCC>" },
-	{    "b tan",         "<CBA>" },
-	{    "b violet",      "<BAD>" },
-
-	{    "",              "<888>" }
+	{    "azure",         "<abd>",  5 },
+	{    "ebony",         "<aaa>",  5 },
+	{    "jade",          "<adb>",  4 },
+	{    "lime",          "<bda>",  4 },
+	{    "orange",        "<dba>",  6 },
+	{    "pink",          "<dab>",  4 },
+	{    "silver",        "<ccc>",  6 },
+	{    "tan",           "<cba>",  3 },
+	{    "violet",        "<bad>",  6 },
+
+	{    "light azure",   "<acf>", 11 },
+	{    "light ebony",   "<bbb>", 11 },
+	{    "light jade",    "<afc>", 10 },
+	{    "light lime",    "<cfa>", 10 },
+	{    "light orange",  "<fca>", 12 },
+	{    "light pink",    "<fac>", 10 },
+	{    "light silver",  "<eee>", 12 },
+	{    "light tan",     "<eda>",  9 },
+	{    "light violet",  "<caf>", 12 },
+
+	{    "light black",   "<108>", 11 },
+	{    "light red",     "<118>",  9 },
+	{    "light green",   "<128>", 11 },
+	{    "light yellow",  "<138>", 12 },
+	{    "light blue",    "<148>", 10 },
+	{    "light magenta", "<158>", 13 },
+	{    "light cyan",    "<168>", 10 },
+	{    "light white",   "<178>", 11 },
+
+	{    "dark black",    "<208>",  5 },
+	{    "dark red",      "<218>",  4 },
+	{    "dark green",    "<228>",  5 },
+	{    "dark yellow",   "<238>",  6 },
+	{    "dark blue",     "<248>",  4 },
+	{    "dark magenta",  "<258>",  7 },
+	{    "dark cyan",     "<268>",  4 },
+	{    "dark white",    "<278>",  5 },
+
+	{    "Azure",         "<acf>",  5 },
+	{    "Ebony",         "<bbb>",  5 },
+	{    "Jade",          "<afc>",  4 },
+	{    "Lime",          "<cfa>",  4 },
+	{    "Orange",        "<fca>",  6 },
+	{    "Pink",          "<fac>",  4 },
+	{    "Silver",        "<eee>",  6 },
+	{    "Tan",           "<eda>",  3 },
+	{    "Violet",        "<caf>",  6 },
+
+	{    "reset",         "<088>",  5 },
+	{    "light",         "<188>",  5 },
+	{    "bold",          "<188>",  4 },
+	{    "faint",         "<288>",  5 },
+	{    "dim",           "<288>",  3 },
+	{    "dark",          "<288>",  4 },
+	{    "underscore",    "<488>", 10 },
+	{    "blink",         "<588>",  5 },
+	{    "reverse",       "<788>",  7 },
+
+	{    "ununderscore", "\e[24m",13 },
+	{    "unblink",      "\e[25m", 8 },
+	{    "unreverse",    "\e[27m",10 },
+
+	{    "black",         "<aaa>",  5 },
+	{    "red",           "<daa>",  4 },
+	{    "green",         "<ada>",  5 },
+	{    "yellow",        "<dda>",  6 },
+	{    "blue",          "<aad>",  4 },
+	{    "magenta",       "<dad>",  7 },
+	{    "cyan",          "<add>",  4 },
+	{    "white",         "<ddd>",  5 },
+
+	{    "Black",         "<bbb>",  5 },
+	{    "Red",           "<faa>",  3 },
+	{    "Green",         "<afa>",  5 },
+	{    "Yellow",        "<ffa>",  6 },
+	{    "Blue",          "<aaf>",  4 },
+	{    "Magenta",       "<faf>",  7 },
+	{    "Cyan",          "<aff>",  4 },
+	{    "White",         "<fff>",  5 },
+
+	{    "b black",       "<AAA>",  7 },
+	{    "b red",         "<DAA>",  5 },
+	{    "b green",       "<ADA>",  7 },
+	{    "b yellow",      "<DDA>",  8 },
+	{    "b blue",        "<AAD>",  6 },
+	{    "b magenta",     "<DAD>",  9 },
+	{    "b cyan",        "<ADD>",  6 },
+	{    "b white",       "<DDD>",  7 },
+
+	{    "b azure",       "<ABD>",  7 },
+	{    "b ebony",       "<AAA>",  7 },
+	{    "b jade",        "<ADB>",  6 },
+	{    "b lime",        "<BDA>",  6 },
+	{    "b orange",      "<DBA>",  8 },
+	{    "b pink",        "<DAB>",  6 },
+	{    "b silver",      "<CCC>",  8 },
+	{    "b tan",         "<CBA>",  5 },
+	{    "b violet",      "<BAD>",  8 },
+
+	{    "b Azure",       "<ACF>",  7 },
+	{    "b Black",       "<BBB>",  7 },
+	{    "b Blue",        "<AAF>",  6 },
+	{    "b Cyan",        "<AFF>",  6 },
+	{    "b Ebony",       "<BBB>",  7 },
+	{    "b Green",       "<AFA>",  7 },
+	{    "b Jade",        "<AFC>",  6 },
+	{    "b Lime",        "<CFA>",  6 },
+	{    "b Magenta",     "<FAF>",  9 },
+	{    "b Orange",      "<FCA>",  8 },
+	{    "b Pink",        "<FAC>",  6 },
+	{    "b Red",         "<FAA>",  5 },
+	{    "b Silver",      "<EEE>",  8 },
+	{    "b Tan",         "<EDA>",  5 },
+	{    "b Violet",      "<CAF>",  8 },
+	{    "b White",       "<FFF>",  7 },
+	{    "b Yellow",      "<FFA>",  8 },
+	{    "",              "<888>",  0 }
 };
 
 struct color_type map_color_table[] =
@@ -519,6 +590,16 @@ struct chat_type chat_table[] =
 	{     "",                 NULL,                0, 0, ""                                               }
 };
 
+struct daemon_type daemon_table[] =
+{
+	{    "ATTACH",            daemon_attach,             "Attach to a daemon"                             },
+	{    "DETACH",            daemon_detach,             "Turn into a daemon and detach"                  },
+	{    "INPUT",             daemon_input,              "Send input to an attached daemon"               },
+	{    "KILL",              daemon_kill,               "Kill a daemon"                                  },
+	{    "LIST",              daemon_list,               "List a daemon"                                  },
+	{    "",                  NULL,                      ""                                               }
+};
+
 struct port_type port_table[] =
 {
 	{     "CALL",             port_call,           0, 0, "Create outgoing socket connection"              },
@@ -546,23 +627,23 @@ struct rank_type rank_table[] =
 
 struct array_type array_table[] =
 {
-	{     "ADD",              array_add           },
-	{     "CLEAR",            array_clear         },
-	{     "CLR",              array_clear         },
-	{     "CREATE",           array_create        },
-	{     "DELETE",           array_delete        },
-	{     "FIND",             array_find          },
-	{     "FND",              array_find          },
-	{     "GET",              array_get           },
-	{     "INSERT",           array_insert        },
-	{     "LENGTH",           array_size          },
-	{     "SET",              array_set           },
-	{     "SIMPLIFY",         array_simplify      },
-	{     "SIZE",             array_size          },
-	{     "SORT",             array_sort          },
-	{     "SRT",              array_sort          },
-	{     "TOKENIZE",         array_tokenize      },
-	{     "",                 NULL                }
+	{     "ADD",              array_add,         "Add an item to a list table"             },
+	{     "CLEAR",            array_clear,       "Clear a list"                            },
+	{     "CLR",              array_clear,       NULL                                      },
+	{     "CREATE",           array_create,      "Create a list table with given items"    },
+	{     "DELETE",           array_delete,      "Delete a list item with given index"     },
+	{     "FIND",             array_find,        "Find a list item with given regex"       },
+	{     "FND",              array_find,        NULL                                      },
+	{     "GET",              array_get,         "Retrieve a list item with given index"   },
+	{     "INSERT",           array_insert,      "Insert a list item at given index"       },
+	{     "LENGTH",           array_size,        NULL                                      },
+	{     "SET",              array_set,         "Change a list item at given index"       },
+	{     "SIMPLIFY",         array_simplify,    "Turn a list table into a simple list"    },
+	{     "SIZE",             array_size,        NULL                                      },
+	{     "SORT",             array_sort,        "Sort a list table alphabetically"        },
+	{     "SRT",              array_sort,        NULL                                      },
+	{     "TOKENIZE",         array_tokenize,    "Create a list with given characters"     },
+	{     "",                 NULL,                                                        }
 };
 
 // 0 no map, 1 has map, 2 is inside map
@@ -570,6 +651,7 @@ struct array_type array_table[] =
 struct map_type map_table[] =
 {
 	{     "AT",               map_at,              0,              2    },
+	{     "CENTER",           map_center,          MAP_FLAG_VTMAP, 2    },
 	{     "COLOR",            map_color,           MAP_FLAG_VTMAP, 1    },
 	{     "CREATE",           map_create,          MAP_FLAG_VTMAP, 0    },
 	{     "DEBUG",            map_debug,           0,              2    },
@@ -892,182 +974,116 @@ struct cursor_type cursor_table[] =
 	},
 
 	{
-		"", "", "\e[5~",  0, cursor_buffer_up
+		"", "", "\e[5~",   0, cursor_buffer_up
 	},
 	{
-		"", "", "\e[6~",  0, cursor_buffer_down
+		"", "", "\e[6~",   0, cursor_buffer_down
 	},
 	{
-		"", "", "",      0, cursor_buffer_lock
+		"", "", "",       0, cursor_buffer_lock
 	},
 	{
-		"", "", "\eOM",   0, cursor_enter
+		"","", "\e[13;2u", 0, cursor_enter
 	},
 	{
-		"", "", "\e[7~",  0, cursor_home
+		"", "", "\eOM",    0, cursor_enter
 	},
 	{
-		"", "", "\e[1~",  0, cursor_home
+		"", "", "\e[7~",   0, cursor_home
 	},
 	{
-		"", "", "\eOH",   0, cursor_home
+		"", "", "\e[1~",   0, cursor_home
 	},
 	{
-		"", "", "\e[H",   0, cursor_home
+		"", "", "\eOH",    0, cursor_home
 	},
 	{
-		"", "", "\eOD",   0, cursor_left
+		"", "", "\e[H",    0, cursor_home
 	},
 	{
-		"", "", "\e[D",   0, cursor_left
+		"", "", "\eOD",    0, cursor_left
 	},
 	{
-		"", "", "\e[8~",  0, cursor_end
+		"", "", "\e[D",    0, cursor_left
 	},
 	{
-		"", "", "\e[4~",  0, cursor_end
+		"", "", "\e[8~",   0, cursor_end
 	},
 	{
-		"", "", "\eOF",   0, cursor_end
+		"", "", "\e[4~",   0, cursor_end
 	},
 	{
-		"", "", "\e[F",   0, cursor_end
+		"", "", "\eOF",    0, cursor_end
 	},
 	{
-		"", "", "\eOC",   0, cursor_right
+		"", "", "\e[F",    0, cursor_end
 	},
 	{
-		"", "", "\e[C",   0, cursor_right
+		"", "", "\eOC",    0, cursor_right
 	},
 	{
-		"", "", "\x7F",   0, cursor_backspace
+		"", "", "\e[C",    0, cursor_right
 	},
 	{
-		"", "", "\eOB",   0, cursor_history_next
+		"", "", "\x7F",    0, cursor_backspace
 	},
 	{
-		"", "", "\e[B",   0, cursor_history_next
+		"", "", "\eOB",    0, cursor_history_next
 	},
 	{
-		"", "", "\eOA",   0, cursor_history_prev
+		"", "", "\e[B",    0, cursor_history_next
 	},
 	{
-		"", "", "\e[A",   0, cursor_history_prev
+		"", "", "\eOA",    0, cursor_history_prev
 	},
 	{
-		"", "", "\e\x7F", 0, cursor_delete_word_left
+		"", "", "\e[A",    0, cursor_history_prev
 	},
 	{
-		"", "", "\ed",    0, cursor_delete_word_right
+		"", "", "\e\x7F",  0, cursor_delete_word_left
 	},
 	{
-		"", "", "",       0, NULL
+		"", "", "\ed",     0, cursor_delete_word_right
+	},
+	{
+		"", "", "",        0, NULL
 	}
 };
 
 struct draw_type draw_table[] =
 {
-	{
-		"BLANK SQUARE",
-		"Draw a blank square.",
-		draw_blank
-	},
-
-	{
-		"BOTTOM SIDE",
-		"Draw the bottom side of a box.",
-		draw_bot_line
-	},
-
 	{
 		"BOX",
 		"Draw a box.",
+		DRAW_FLAG_BOXED|DRAW_FLAG_LEFT|DRAW_FLAG_RIGHT|DRAW_FLAG_TOP|DRAW_FLAG_BOT,
 		draw_box
 	},
 
 	{
-		"BOX TEXT",
-		"Draw a box with given text.",
-		draw_box_text
-	},
-
-	{
-		"CENTER LEFT LINE",
-		"Draw the center left line of two boxes.",
-		draw_center_left_line
-	},
-
-	{
-		"CENTER RIGHT LINE",
-		"Draw the center right line of two boxes.",
-		draw_center_right_line
-	},
-
-	{
-		"HORIZONTAL LINE",
-		"Draw a horizontal line.",
-		draw_horizontal_line
-	},
-
-	{
-		"LEFT SIDE",
-		"Draw the left side of a box.",
-		draw_left_line
-	},
-
-
-	{
-		"MIDDLE TOP LINE",
-		"Draw the middle top line of two boxes.",
-		draw_middle_top_line
-	},
-
-	{
-		"MIDDLE BOTTOM LINE",
-		"Draw the middle bottom line of two boxes.",
-		draw_middle_bot_line
-	},
-
-	{
-		"RIGHT SIDE",
-		"Draw the right side of a box.",
-		draw_right_line
-	},
-	
-	{
-		"SIDE LINES",
-		"Draw the left and right sides of a box.",
-		draw_side_lines
-	},
-
-	{
-		"SIDE LINES TEXT",
-		"Draw the side lines of a box with given text.",
-		draw_side_lines_text
-	},
-
-
-	{
-		"TOP LINE",
-		"Draw the bottom lines of a box.",
-		draw_top_line
+		"LINE",
+		"Draw a line.",
+		DRAW_FLAG_NONE,
+		draw_line
 	},
 
 	{
-		"TEXT",
-		"Draw given text without a frame.",
-		draw_text
+		"SIDE",
+		"Draw the side of a box.",
+		DRAW_FLAG_BOXED,
+		draw_side
 	},
 
 	{
-		"VERTICAL LINE",
-		"Draw a vertical line.",
-		draw_vertical_line
+		"TILE",
+		"Draw a tile.",
+		0,
+		draw_square
 	},
 
 	{
 		"",
 		"",
+		0,
 		NULL
 	}
 };
@@ -1098,6 +1114,15 @@ struct screen_type screen_table[] =
 		SCREEN_FLAG_CSIP,
 		screen_cursor
 	},
+
+	{
+		"FILL",
+		"Fill given region with given character.",
+		SCREEN_FLAG_GET_ONE,
+		SCREEN_FLAG_GET_ONE,
+		SCREEN_FLAG_CSIP,
+		screen_fill
+	},
 	{
 		"FOCUS",
 		"Shuffle the screen to the front of the desktop.",
@@ -1173,7 +1198,7 @@ struct screen_type screen_table[] =
 	},
 	{
 		"REFRESH",
-		"Refresh the screen. (may not do much)",
+		"Force a refresh of the screen.",
 		SCREEN_FLAG_GET_ONE,
 		SCREEN_FLAG_GET_ONE,
 		SCREEN_FLAG_CSIP,
@@ -1203,6 +1228,16 @@ struct screen_type screen_table[] =
 		SCREEN_FLAG_CSIP,
 		screen_save
 	},
+
+	{
+		"SCROLL",
+		"Set the scroll region to {square}.",
+		SCREEN_FLAG_GET_ONE,
+		SCREEN_FLAG_GET_ONE,
+		SCREEN_FLAG_CSIP,
+		screen_scrollregion
+	},
+
 	{
 		"SCROLLBAR",
 		"Scrollbar settings.",
@@ -1211,6 +1246,8 @@ struct screen_type screen_table[] =
 		SCREEN_FLAG_CSIP,
 		screen_scrollbar
 	},
+
+
 	{
 		"SET",
 		"Set screen information.",
@@ -1227,14 +1264,14 @@ struct screen_type screen_table[] =
 
 struct timer_type timer_table[] =
 {
-	{    "Poll Stdin"                  },
-	{    "Poll Sessions"               },
-	{    "Poll Chat Server"            },
-	{    "Poll Port Sessions"          },
-	{    "Update Tickers"              },
+	{    "Update Input"                },
+	{    "Update Sessions"             },
 	{    "Update Delays"               },
+	{    "Update Chat"                 },
+	{    "Update Port"                 },
+	{    "Update Tickers"              },
+	{    "Update Paths"                },
 	{    "Update Packet Patcher"       },
-	{    "Update Chat Server"          },
 	{    "Update Terminal"             },
 	{    "Update Time Events"          },
 	{    "Update Memory"               },
@@ -1243,85 +1280,94 @@ struct timer_type timer_table[] =
 
 struct event_type event_table[] =
 {
-	{    "CATCH ",                                 "Triggers on catch events."               },
-	{    "CHAT MESSAGE",                           "Triggers on any chat related message."   },
-	{    "CLASS ACTIVATED",                        "Triggers on class activations."          },
-	{    "CLASS CREATED",                          "Triggers on class creation."             },
-	{    "CLASS DEACTIVATED",                      "Triggers on class deactivations."        },
-	{    "CLASS DESTROYED",                        "Triggers on class destruction."          },
-	{    "DATE",                                   "Triggers on the given date."             },
-	{    "DAY",                                    "Triggers each day or given day."         },
-	{    "DOUBLE-CLICKED ",                        "Triggers when mouse is double-clicked"   },
-	{    "END OF PATH",                            "Triggers when walking the last room."    },
-	{    "HOUR",                                   "Triggers each hour or given hour."       },
-	{    "IAC ",                                   "Triggers on telopt negotiation."         },
-	{    "LONG-CLICKED ",                          "Triggers when mouse is long-clicked."    },
-	{    "MAP DOUBLE-CLICKED ",                    "Triggers on vt map click."               },
-	{    "MAP ENTER MAP",                          "Triggers when entering the map."         },
-	{    "MAP ENTER ROOM",                         "Triggers when entering a map room."      },
-	{    "MAP EXIT MAP",                           "Triggers when exiting the map."          },
-	{    "MAP EXIT ROOM",                          "Triggers when exiting a map room."       },
-	{    "MAP FOLLOW MAP",                         "Triggers when moving to a map room."     },
-	{    "MAP LONG-CLICKED ",                      "Triggers on vt map click."               },
-	{    "MAP PRESSED ",                           "Triggers on vt map click."               },
-	{    "MAP RELEASED ",                          "Triggers on vt map click."               },
-	{    "MAP SHORT-CLICKED ",                     "Triggers on vt map click."               },
-	{    "MAP TRIPLE-CLICKED ",                    "Triggers on vt map click."               },
-	{    "MAP UPDATED VTMAP",                      "Triggers on vt map update."              },
-	{    "MINUTE",                                 "Triggers each minute or given minute."   },
-	{    "MONTH",                                  "Triggers each month or given month."     },
-	{    "MOVED ",                                 "Triggers when mouse is moved."           },
-	{    "PORT CONNECTION",                        "Triggers when socket connects."          },
-	{    "PORT DISCONNECTION",                     "Triggers when socket disconnects."       },
-	{    "PORT LOG MESSAGE",                       "Triggers on local port log messages."    },
-	{    "PORT MESSAGE",                           "Triggers on local port messages."        },
-	{    "PORT RECEIVED MESSAGE",                  "Triggers when socket data is received."  },
-	{    "PRESSED ",                               "Triggers when mouse button is pressed."  },
-	{    "PROGRAM START",                          "Triggers when main session starts."      },
-	{    "PROGRAM TERMINATION",                    "Triggers when main session exists."      },
-	{    "RECEIVED INPUT",                         "Triggers when new input is received."    },
-	{    "RECEIVED KEYPRESS",                      "Triggers when a keypress is received."   },
-	{    "RECEIVED LINE",                          "Triggers when a new line is received."   },
-	{    "RECEIVED OUTPUT",                        "Triggers when new output is received."   },
-	{    "RECEIVED PROMPT",                        "Triggers when a prompt is received."     },
-	{    "RELEASED ",                              "Triggers when mouse button is released." },
-	{    "SCAN CSV HEADER",                        "Triggers when scanning a csv file."      },
-	{    "SCAN CSV LINE",                          "Triggers when scanning a csv file."      },
-	{    "SCAN TSV HEADER",                        "Triggers when scanning a tsv file."      },
-	{    "SCAN TSV LINE",                          "Triggers when scanning a tsv file."      },
-	{    "SCREEN CHARACTER DIMENSIONS",            "Triggers when called by #screen raise."  },
-	{    "SCREEN DESKTOP DIMENSIONS",              "Triggers when called by #screen raise."  },
-	{    "SCREEN DIMENSIONS",                      "Triggers when called by #screen raise."  },
-	{    "SCREEN MINIMIZED",                       "Triggers when called by #screen raise."  },
-	{    "SCREEN POSITION",                        "Triggers when called by #screen raise."  },
-	{    "SCREEN RESIZE",                          "Triggers when the screen is resized."    },
-	{    "SCROLLED ",                              "Triggers when mouse wheel is scrolled."  },
-	{    "SECOND",                                 "Triggers each second or given second."   },
-	{    "SEND OUTPUT",                            "Triggers before sending output."         },
-	{    "SENT OUTPUT",                            "Triggers after sending output."          },
-	{    "SESSION ACTIVATED",                      "Triggers when a session is activated."   },
-	{    "SESSION CONNECTED",                      "Triggers when a new session connects."   },
-	{    "SESSION CREATED",                        "Triggers when a new session is created." },
-	{    "SESSION DEACTIVATED",                    "Triggers when a session is deactivated." },
-	{    "SESSION DISCONNECTED",                   "Triggers when a session disconnects."    },
-	{    "SESSION TIMED OUT",                      "Triggers when a session doesn't connect."},
-	{    "SHORT-CLICKED",                          "Triggers when mouse is short-clicked."   },
-	{    "SYSTEM ERROR",                           "Triggers on system errors."              },
-	{    "TIME",                                   "Triggers on the given time."             },
-	{    "TRIPLE-CLICKED",                         "Triggers when mouse is triple-clicked."  },
-	{    "UNKNOWN COMMAND",                        "Triggers on unknown tintin command."     },
-	{    "VARIABLE UPDATE ",                       "Triggers on a variable update."          },
-	{    "VT100 CPR",                              "Triggers on an ESC [ 6 n call."          },
-	{    "VT100 DA",                               "Triggers on an ESC [ c call."            },
-	{    "VT100 DECID",                            "Triggers on an ESC Z call."              },
-	{    "VT100 DSR",                              "Triggers on an ESC [ 5 n call."          },
-	{    "VT100 ENQ",                              "Triggers on an \\x05 call."              },
-	{    "VT100 SCROLL REGION",                    "Triggers on vt100 scroll region change." },
-	{    "WEEK",                                   "Triggers each week or given week."       },
-	{    "WINDOW FOCUS IN",                        "Triggers on window focussing in."        },
-	{    "WINDOW FOCUS OUT",                       "Triggers on window focussing out."       },
-	{    "YEAR",                                   "Triggers each year or given year."       },
-	{    "",                                       ""                                        }
+	{    "CATCH ",                                 EVENT_FLAG_CATCH,    "Triggers on catch events."               },
+	{    "CHAT MESSAGE",                           EVENT_FLAG_PORT,     "Triggers on any chat related message."   },
+	{    "CLASS ACTIVATED",                        EVENT_FLAG_CLASS,    "Triggers on class activations."          },
+	{    "CLASS CREATED",                          EVENT_FLAG_CLASS,    "Triggers on class creation."             },
+	{    "CLASS DEACTIVATED",                      EVENT_FLAG_CLASS,    "Triggers on class deactivations."        },
+	{    "CLASS DESTROYED",                        EVENT_FLAG_CLASS,    "Triggers on class destruction."          },
+	{    "DATE",                                   EVENT_FLAG_TIME,     "Triggers on the given date."             },
+	{    "DAY",                                    EVENT_FLAG_TIME,     "Triggers each day or given day."         },
+	{    "DOUBLE-CLICKED ",                        EVENT_FLAG_MOUSE,    "Triggers when mouse is double-clicked"   },
+	{    "END OF PATH",                            EVENT_FLAG_MAP,      "Triggers when walking the last room."    },
+	{    "HOUR",                                   EVENT_FLAG_TIME,     "Triggers each hour or given hour."       },
+	{    "IAC ",                                   EVENT_FLAG_TELNET,   "Triggers on telopt negotiation."         },
+	{    "LONG-CLICKED ",                          EVENT_FLAG_MOUSE,    "Triggers when mouse is long-clicked."    },
+	{    "MAP DOUBLE-CLICKED ",                    EVENT_FLAG_MOUSE,    "Triggers on vt map click."               },
+	{    "MAP ENTER MAP",                          EVENT_FLAG_MAP,      "Triggers when entering the map."         },
+	{    "MAP ENTER ROOM",                         EVENT_FLAG_MAP,      "Triggers when entering a map room."      },
+	{    "MAP EXIT MAP",                           EVENT_FLAG_MAP,      "Triggers when exiting the map."          },
+	{    "MAP EXIT ROOM",                          EVENT_FLAG_MAP,      "Triggers when exiting a map room."       },
+	{    "MAP FOLLOW MAP",                         EVENT_FLAG_MAP,      "Triggers when moving to a map room."     },
+	{    "MAP LONG-CLICKED ",                      EVENT_FLAG_MAP,      "Triggers on vt map click."               },
+	{    "MAP PRESSED ",                           EVENT_FLAG_MAP,      "Triggers on vt map click."               },
+	{    "MAP RELEASED ",                          EVENT_FLAG_MAP,      "Triggers on vt map click."               },
+	{    "MAP SHORT-CLICKED ",                     EVENT_FLAG_MAP,      "Triggers on vt map click."               },
+	{    "MAP TRIPLE-CLICKED ",                    EVENT_FLAG_MAP,      "Triggers on vt map click."               },
+	{    "MAP UPDATED VTMAP",                      EVENT_FLAG_MAP,      "Triggers on vt map update."              },
+	{    "MINUTE",                                 EVENT_FLAG_TIME,     "Triggers each minute or given minute."   },
+	{    "MONTH",                                  EVENT_FLAG_TIME,     "Triggers each month or given month."     },
+	{    "MOVED ",                                 EVENT_FLAG_MOUSE,    "Triggers when mouse is moved."           },
+	{    "PORT CONNECTION",                        EVENT_FLAG_PORT,     "Triggers when socket connects."          },
+	{    "PORT DISCONNECTION",                     EVENT_FLAG_PORT,     "Triggers when socket disconnects."       },
+	{    "PORT LOG MESSAGE",                       EVENT_FLAG_PORT,     "Triggers on local port log messages."    },
+	{    "PORT MESSAGE",                           EVENT_FLAG_PORT,     "Triggers on local port messages."        },
+	{    "PORT RECEIVED MESSAGE",                  EVENT_FLAG_PORT,     "Triggers when socket data is received."  },
+	{    "PRESSED ",                               EVENT_FLAG_MOUSE,    "Triggers when mouse button is pressed."  },
+	{    "PROGRAM START",                          EVENT_FLAG_SYSTEM,   "Triggers when main session starts."      },
+	{    "PROGRAM TERMINATION",                    EVENT_FLAG_SYSTEM,   "Triggers when main session exists."      },
+	{    "READ ERROR",                             EVENT_FLAG_SYSTEM,   "Triggers when the read command fails."   },
+	{    "RECEIVED INPUT",                         EVENT_FLAG_INPUT,    "Triggers when new input is received."    },
+	{    "RECEIVED KEYPRESS",                      EVENT_FLAG_INPUT,    "Triggers when a keypress is received."   },
+	{    "RECEIVED LINE",                          EVENT_FLAG_OUTPUT,   "Triggers when a new line is received."   },
+	{    "RECEIVED OUTPUT",                        EVENT_FLAG_OUTPUT,   "Triggers when new output is received."   },
+	{    "RECEIVED PROMPT",                        EVENT_FLAG_OUTPUT,   "Triggers when a prompt is received."     },
+	{    "RELEASED ",                              EVENT_FLAG_MOUSE,    "Triggers when mouse button is released." },
+	{    "SCAN CSV HEADER",                        EVENT_FLAG_SCAN,     "Triggers when scanning a csv file."      },
+	{    "SCAN CSV LINE",                          EVENT_FLAG_SCAN,     "Triggers when scanning a csv file."      },
+	{    "SCAN TSV HEADER",                        EVENT_FLAG_SCAN,     "Triggers when scanning a tsv file."      },
+	{    "SCAN TSV LINE",                          EVENT_FLAG_SCAN,     "Triggers when scanning a tsv file."      },
+	{    "SCREEN DESKTOP DIMENSIONS",              EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
+	{    "SCREEN DESKTOP SIZE",                    EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
+	{    "SCREEN DIMENSIONS",                      EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
+	{    "SCREEN MINIMIZED",                       EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
+	{    "SCREEN POSITION",                        EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
+	{    "SCREEN REFRESH",                         EVENT_FLAG_SCREEN,   "Triggers when the screen is refreshed."  },
+	{    "SCREEN RESIZE",                          EVENT_FLAG_SCREEN,   "Triggers when the screen is resized."    },
+	{    "SCREEN ROTATE LANDSCAPE",                EVENT_FLAG_SCREEN,   "Triggers when the screen is rotated."    },
+	{    "SCREEN ROTATE PORTRAIT",                 EVENT_FLAG_SCREEN,   "Triggers when the screen is rotated."    },
+	{    "SCREEN SIZE",                            EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
+	{    "SCREEN SPLIT",                           EVENT_FLAG_SCREEN,   "Triggers when the screen is split."      },
+	{    "SCREEN UNSPLIT",                         EVENT_FLAG_SCREEN,   "Triggers when the screen is unsplit."    },
+	{    "SCROLLED ",                              EVENT_FLAG_MOUSE,    "Triggers when mouse wheel is scrolled."  },
+	{    "SECOND",                                 EVENT_FLAG_TIME,     "Triggers each second or given second."   },
+	{    "SEND OUTPUT",                            EVENT_FLAG_INPUT,    "Triggers before sending output."         },
+	{    "SENT OUTPUT",                            EVENT_FLAG_INPUT,    "Triggers after sending output."          },
+	{    "SESSION ACTIVATED",                      EVENT_FLAG_SESSION,  "Triggers when a session is activated."   },
+	{    "SESSION CONNECTED",                      EVENT_FLAG_SESSION,  "Triggers when a new session connects."   },
+	{    "SESSION CREATED",                        EVENT_FLAG_SESSION,  "Triggers when a new session is created." },
+	{    "SESSION DEACTIVATED",                    EVENT_FLAG_SESSION,  "Triggers when a session is deactivated." },
+	{    "SESSION DISCONNECTED",                   EVENT_FLAG_SESSION,  "Triggers when a session disconnects."    },
+	{    "SESSION TIMED OUT",                      EVENT_FLAG_SESSION,  "Triggers when a session doesn't connect."},
+	{    "SHORT-CLICKED",                          EVENT_FLAG_MOUSE,    "Triggers when mouse is short-clicked."   },
+	{    "SYSTEM ERROR",                           EVENT_FLAG_SYSTEM,   "Triggers on system errors."              },
+	{    "TIME",                                   EVENT_FLAG_TIME,     "Triggers on the given time."             },
+	{    "TRIPLE-CLICKED",                         EVENT_FLAG_MOUSE,    "Triggers when mouse is triple-clicked."  },
+	{    "UNKNOWN COMMAND",                        EVENT_FLAG_SYSTEM,   "Triggers on unknown tintin command."     },
+	{    "VARIABLE UPDATE ",                       EVENT_FLAG_SYSTEM,   "Triggers before a variable updates."     },
+	{    "VARIABLE UPDATED ",                      EVENT_FLAG_SYSTEM,   "Triggers after a variable update."       },
+	{    "VT100 CPR",                              EVENT_FLAG_VT100,    "Triggers on an ESC [ 6 n call."          },
+	{    "VT100 DA",                               EVENT_FLAG_VT100,    "Triggers on an ESC [ c call."            },
+	{    "VT100 DECID",                            EVENT_FLAG_VT100,    "Triggers on an ESC Z call."              },
+	{    "VT100 DSR",                              EVENT_FLAG_VT100,    "Triggers on an ESC [ 5 n call."          },
+	{    "VT100 ENQ",                              EVENT_FLAG_VT100,    "Triggers on an \\x05 call."              },
+	{    "VT100 SCROLL REGION",                    EVENT_FLAG_VT100,    "Triggers on vt100 scroll region change." },
+	{    "WEEK",                                   EVENT_FLAG_TIME,     "Triggers each week or given week."       },
+	{    "WINDOW FOCUS IN",                        EVENT_FLAG_SCREEN,   "Triggers on window focussing in."        },
+	{    "WINDOW FOCUS OUT",                       EVENT_FLAG_SCREEN,   "Triggers on window focussing out."       },
+	{    "WRITE ERROR",                            EVENT_FLAG_SYSTEM,   "Triggers when the write command fails."  },
+	{    "YEAR",                                   EVENT_FLAG_TIME,     "Triggers each year or given year."       },
+	{    "",                                       0,                   ""                                        }
 };
 
 struct path_type path_table[] =
@@ -1352,18 +1398,20 @@ struct path_type path_table[] =
 
 struct line_type line_table[] =
 {
-	{    "BACKGROUND",        line_background        },
-	{    "GAG",               line_gag               },
-	{    "IGNORE",            line_ignore            },
-	{    "LOG",               line_log               },
-	{    "LOGMODE",           line_logmode           },
-	{    "LOGVERBATIM",       line_logverbatim       },
-	{    "QUIET",             line_quiet             },
-	{    "STRIP",             line_strip             },
-	{    "SUBSTITUTE",        line_substitute        },
-	{    "VERBATIM",          line_verbatim          },
-	{    "VERBOSE",           line_verbose           },
-	{    "",                  NULL                   }
+	{    "BACKGROUND",        line_background,     "Execute line without stealing session focus."   },
+	{    "DEBUG",             line_debug,          "Execute line in debug mode."                    },
+	{    "GAG",               line_gag,            "Gag the next line."                             },
+	{    "IGNORE",            line_ignore,         "Execute line with triggers ignored."            },
+	{    "LOG",               line_log,            "Log the next line or given line."               },
+	{    "LOGMODE",           line_logmode,        "Execute line with given log mode."              },
+	{    "LOGVERBATIM",       line_logverbatim,    "Log the line as plain text verbatim."           },
+	{    "ONESHOT",           line_oneshot,        "Execute line creating oneshot triggers."        },
+	{    "QUIET",             line_quiet,          "Execute line with all system messages off."     },
+	{    "STRIP",             line_strip,          "Execute line with escape codes stripped."       },
+	{    "SUBSTITUTE",        line_substitute,     "Execute line with given substitution."          },
+	{    "VERBATIM",          line_verbatim,       "Execute line as plain text."                    },
+	{    "VERBOSE",           line_verbose,        "Execute line with all system messages on."      },
+	{    "",                  NULL,                ""                                               }
 };
 
 struct history_type history_table[] =

+ 67 - 47
src/telopt_client.c

@@ -59,6 +59,8 @@ extern  int  client_send_dont_mccp2(struct session *ses, int cplen, unsigned cha
 extern  int  client_init_mccp2(struct session *ses, int cplen, unsigned char *cpsrc);
 extern  int  client_recv_will_mccp3(struct session *ses, int cplen, unsigned char *cpsrc);
 extern  int  client_recv_dont_mccp3(struct session *ses, int cplen, unsigned char *cpsrc);
+extern  int  client_recv_wont_mccp3(struct session *ses, int cplen, unsigned char *cpsrc);
+
 extern  int  client_init_mccp3(struct session *ses);
 extern void  client_end_mccp3(struct session *ses);
 extern  int  client_skip_sb(struct session *ses, int cplen, unsigned char *cpsrc);
@@ -82,6 +84,7 @@ struct iac_type iac_client_table [] =
 	{   3,  (unsigned char []) {IAC, WILL, TELOPT_MCCP2},                     &client_recv_will_mccp2         },
 	{   3,  (unsigned char []) {IAC, WILL, TELOPT_MCCP3},                     &client_recv_will_mccp3         },
 	{   3,  (unsigned char []) {IAC, DONT, TELOPT_MCCP3},                     &client_recv_dont_mccp3         },
+	{   3,  (unsigned char []) {IAC, WONT, TELOPT_MCCP3},                     &client_recv_wont_mccp3         },
 	{   3,  (unsigned char []) {IAC, WILL, TELOPT_MSSP},                      &client_recv_will_mssp          },
 	{   3,  (unsigned char []) {IAC, SB,   TELOPT_MSSP},                      &client_recv_sb_mssp            },
 	{   3,  (unsigned char []) {IAC, SB,   TELOPT_MSDP},                      &client_recv_sb_msdp            },
@@ -131,28 +134,28 @@ int client_translate_telopts(struct session *ses, unsigned char *src, int cplen)
 		return 0;
 	}
 
-	if (ses->mccp)
+	if (ses->mccp2)
 	{
-		ses->mccp->next_in   = src;
-		ses->mccp->avail_in  = cplen;
+		ses->mccp2->next_in   = src;
+		ses->mccp2->avail_in  = cplen;
 
-		ses->mccp->next_out  = gtd->mccp_buf;
-		ses->mccp->avail_out = gtd->mccp_len;
+		ses->mccp2->next_out  = gtd->mccp_buf;
+		ses->mccp2->avail_out = gtd->mccp_len;
 
 		inflate:
 
-		retval = inflate(ses->mccp, Z_SYNC_FLUSH);
+		retval = inflate(ses->mccp2, Z_SYNC_FLUSH);
 
 		switch (retval)
 		{
 			case Z_BUF_ERROR:
-				if (ses->mccp->avail_out == 0)
+				if (ses->mccp2->avail_out == 0)
 				{
 					gtd->mccp_len *= 2;
 					gtd->mccp_buf  = (unsigned char *) realloc(gtd->mccp_buf, gtd->mccp_len);
 
-					ses->mccp->avail_out = gtd->mccp_len / 2;
-					ses->mccp->next_out  = gtd->mccp_buf + gtd->mccp_len / 2;
+					ses->mccp2->avail_out = gtd->mccp_len / 2;
+					ses->mccp2->next_out  = gtd->mccp_buf + gtd->mccp_len / 2;
 
 					goto inflate;
 				}
@@ -161,40 +164,40 @@ int client_translate_telopts(struct session *ses, unsigned char *src, int cplen)
 					tintin_puts2(ses, "");
 					tintin_puts2(ses, "#COMPRESSION ERROR, Z_BUF_ERROR, DISABLING MCCP2.");
 					client_send_dont_mccp2(ses, 0, NULL);
-					inflateEnd(ses->mccp);
-					free(ses->mccp);
-					ses->mccp = NULL;
+					inflateEnd(ses->mccp2);
+					free(ses->mccp2);
+					ses->mccp2 = NULL;
 					cpsrc = src;
 					cplen = 0;
 				}
 				break;
 
 			case Z_OK:
-				if (ses->mccp->avail_out == 0)
+				if (ses->mccp2->avail_out == 0)
 				{
 					gtd->mccp_len *= 2;
 					gtd->mccp_buf  = (unsigned char *) realloc(gtd->mccp_buf, gtd->mccp_len);
 
-					ses->mccp->avail_out = gtd->mccp_len / 2;
-					ses->mccp->next_out  = gtd->mccp_buf + gtd->mccp_len / 2;
+					ses->mccp2->avail_out = gtd->mccp_len / 2;
+					ses->mccp2->next_out  = gtd->mccp_buf + gtd->mccp_len / 2;
 
 					goto inflate;
 				}
-				cplen = ses->mccp->next_out - gtd->mccp_buf;
+				cplen = ses->mccp2->next_out - gtd->mccp_buf;
 				cpsrc = gtd->mccp_buf;
 				break;
 
 			case Z_STREAM_END:
 				client_telopt_debug(ses, "#COMPRESSION END, DISABLING MCCP2.");
 
-				cnt = ses->mccp->next_out - gtd->mccp_buf;
+				cnt = ses->mccp2->next_out - gtd->mccp_buf;
 
-				cpsrc = src + (cplen - ses->mccp->avail_in);
-				cplen = ses->mccp->avail_in;
+				cpsrc = src + (cplen - ses->mccp2->avail_in);
+				cplen = ses->mccp2->avail_in;
 
-				inflateEnd(ses->mccp);
-				free(ses->mccp);
-				ses->mccp = NULL;
+				inflateEnd(ses->mccp2);
+				free(ses->mccp2);
+				ses->mccp2 = NULL;
 
 				client_translate_telopts(ses, gtd->mccp_buf, cnt);
 				break;
@@ -203,9 +206,9 @@ int client_translate_telopts(struct session *ses, unsigned char *src, int cplen)
 				tintin_puts2(ses, "");
 				tintin_printf2(ses, "#COMPRESSION ERROR, DISABLING MCCP2, RETVAL %d.", retval);
 				client_send_dont_mccp2(ses, 0, NULL);
-				inflateEnd(ses->mccp);
-				free(ses->mccp);
-				ses->mccp = NULL;
+				inflateEnd(ses->mccp2);
+				free(ses->mccp2);
+				ses->mccp2 = NULL;
 				cpsrc = src;
 				cplen = 0;
 				break;
@@ -219,8 +222,6 @@ int client_translate_telopts(struct session *ses, unsigned char *src, int cplen)
 	if (HAS_BIT(ses->logmode, LOG_FLAG_LOW) && ses->logfile)
 	{
 		fwrite(cpsrc, 1, cplen, ses->logfile);
-
-		fflush(ses->logfile);
 	}
 
  	if (ses->read_len + cplen >= ses->read_max)
@@ -440,7 +441,7 @@ int client_translate_telopts(struct session *ses, unsigned char *src, int cplen)
 					cplen--;
 					continue;
 
-				case 5:
+				case ASCII_ENQ:
 					check_all_events(ses, SUB_ARG, 0, 1, "VT100 ENQ", gtd->term);
 					cpsrc++;
 					cplen--;
@@ -525,7 +526,7 @@ int client_translate_telopts(struct session *ses, unsigned char *src, int cplen)
 
 							for (skip = 2 ; cplen >= skip ; skip++)
 							{
-								if (cpsrc[skip] == '\a')
+								if (cpsrc[skip] == ASCII_BEL)
 								{
 									break;
 								}
@@ -669,12 +670,12 @@ int client_recv_sb_ttype(struct session *ses, int cplen, unsigned char *cpsrc)
 		char mtts[BUFFER_SIZE];
 
 		sprintf(mtts, "MTTS %d",
-			(HAS_BIT(ses->flags, SES_FLAG_ANSICOLOR) ? 1 : 0) +
+			(ses->color > 0 ? 1 : 0) +
 			(HAS_BIT(ses->flags, SES_FLAG_SPLIT) ? 0 : 2) +
-			(HAS_BIT(ses->flags, SES_FLAG_UTF8) && !HAS_BIT(ses->flags, SES_FLAG_BIG5TOUTF8) ? 4 : 0) +
-			(HAS_BIT(ses->flags, SES_FLAG_256COLOR) ? 8 : 0) +
+			(HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && !HAS_BIT(ses->charset, CHARSET_FLAG_BIG5TOUTF8) ? 4 : 0) +
+			(ses->color > 16 ? 8 : 0) +
 			(HAS_BIT(ses->flags, SES_FLAG_SCREENREADER) ? 64 : 0) +
-			(HAS_BIT(ses->flags, SES_FLAG_TRUECOLOR) ? 256 : 0));
+			(ses->color > 256 ? 256 : 0));
 
 		telnet_printf(ses, 6 + strlen(mtts), "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 0, mtts, IAC, SE);
 
@@ -755,9 +756,9 @@ int client_send_sb_naws(struct session *ses, int cplen, unsigned char *cpsrc)
 	int rows;
 	int cols;
 
-	rows = HAS_BIT(ses->flags, SES_FLAG_SPLIT) ? ses->bot_row - ses->top_row + 1 : gtd->screen->rows;
+	rows = HAS_BIT(ses->flags, SES_FLAG_SPLIT) ? ses->split->bot_row - ses->split->top_row + 1 : gtd->screen->rows;
 
-	cols = ses->wrap > 0 ? ses->wrap : gtd->screen->cols;
+	cols = get_scroll_cols(ses);
 
 	// Properly handle row and colum size of 255
 
@@ -1123,6 +1124,9 @@ int client_recv_sb_msdp(struct session *ses, int cplen, unsigned char *src)
 				last = MSDP_VAL;
 				break;
 
+			case '\r':
+				break;
+
 			case '\\':
 				*pto++ = '\\';
 				*pto++ = '\\';
@@ -1390,7 +1394,7 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 			{
 				if (!strcasecmp(var, "UTF-8"))
 				{
-					if (HAS_BIT(ses->flags, SES_FLAG_UTF8) && !HAS_BIT(ses->flags, SES_FLAG_BIG5TOUTF8) && !HAS_BIT(ses->flags, SES_FLAG_FANSITOUTF8))
+					if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && !HAS_BIT(ses->charset, CHARSET_FLAG_BIG5TOUTF8) && !HAS_BIT(ses->charset, CHARSET_FLAG_FANSITOUTF8))
 					{
 						telnet_printf(ses, 12, "%c%c%c%c UTF-8%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_ACCEPTED, IAC, SE);
 
@@ -1405,7 +1409,7 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 				}
 				else if (!strcasecmp(var, "BIG-5"))
 				{
-					if (HAS_BIT(ses->flags, SES_FLAG_BIG5) || HAS_BIT(ses->flags, SES_FLAG_BIG5TOUTF8))
+					if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) || HAS_BIT(ses->charset, CHARSET_FLAG_BIG5TOUTF8))
 					{
 						telnet_printf(ses, 11, "%c%c%c%c BIG-5%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_ACCEPTED, IAC, SE);
 
@@ -1422,7 +1426,7 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 				{
 					if (!check_all_events(ses, SUB_ARG|SUB_SEC, 2, 2, "CATCH IAC SB CHARSET %s %s", buf, var, buf, var))
 					{
-						if (HAS_BIT(ses->flags, SES_FLAG_FANSITOUTF8))
+						if (HAS_BIT(ses->charset, CHARSET_FLAG_FANSITOUTF8))
 						{
 							telnet_printf(ses, 11, "%c%c%c%c FANSI%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_ACCEPTED, IAC, SE);
 
@@ -1914,24 +1918,24 @@ int client_send_dont_mccp2(struct session *ses, int cplen, unsigned char *cpsrc)
 
 int client_init_mccp2(struct session *ses, int cplen, unsigned char *cpsrc)
 {
-	if (ses->mccp)
+	if (ses->mccp2)
 	{
 		return 5;
 	}
 
-	ses->mccp = (z_stream *) calloc(1, sizeof(z_stream));
+	ses->mccp2 = (z_stream *) calloc(1, sizeof(z_stream));
 
-	ses->mccp->data_type = Z_ASCII;
-	ses->mccp->zalloc    = zlib_alloc;
-	ses->mccp->zfree     = zlib_free;
-	ses->mccp->opaque    = NULL;
+	ses->mccp2->data_type = Z_ASCII;
+	ses->mccp2->zalloc    = zlib_alloc;
+	ses->mccp2->zfree     = zlib_free;
+	ses->mccp2->opaque    = NULL;
 
-	if (inflateInit(ses->mccp) != Z_OK)
+	if (inflateInit(ses->mccp2) != Z_OK)
 	{
 		tintin_puts2(ses, "MCCP2: FAILED TO INITIALIZE");
 		client_send_dont_mccp2(ses, 0, NULL);
-		free(ses->mccp);
-		ses->mccp = NULL;
+		free(ses->mccp2);
+		ses->mccp2 = NULL;
 	}
 	else
 	{
@@ -1987,6 +1991,22 @@ int client_recv_dont_mccp3(struct session *ses, int cplen, unsigned char *cpsrc)
 	return 3;
 }
 
+int client_recv_wont_mccp3(struct session *ses, int cplen, unsigned char *cpsrc)
+{
+	if (check_all_events(ses, SUB_ARG|SUB_SEC, 0, 0, "CATCH IAC WONT MCCP3"))
+	{
+	 	return 3;
+	}
+
+	check_all_events(ses, SUB_ARG|SUB_SEC, 0, 0, "IAC WONT MCCP3");
+
+	if (ses->mccp3)
+	{
+		client_end_mccp3(ses);
+	}
+	return 3;
+}
+
 int client_init_mccp3(struct session *ses)
 {
 	z_stream *stream;

+ 0 - 6
src/telopt_server.c

@@ -383,7 +383,6 @@ int server_translate_telopts(struct session *ses, struct port_data *buddy, unsig
 	return strlen((char *) out);
 }
 
-
 void telopt_debug(struct session *ses, char *format, ...)
 {
 	char buf[BUFFER_SIZE];
@@ -1070,7 +1069,6 @@ void write_mccp2(struct session *ses, struct port_data *buddy, char *txt, int le
 	return;
 }
 
-
 void process_mccp2(struct session *ses, struct port_data *buddy)
 {
 	if (HAS_BIT(buddy->flags, PORT_FLAG_LINKLOST))
@@ -1093,7 +1091,6 @@ int process_do_mccp2(struct session *ses, struct port_data *buddy, unsigned char
 	return 3;
 }
 
-
 int process_dont_mccp2(struct session *ses, struct port_data *buddy, unsigned char *src, int srclen )
 {
 	end_mccp2(ses, buddy);
@@ -1150,8 +1147,6 @@ void end_mccp3(struct session *ses, struct port_data *buddy)
 	}
 }
 
-
-
 int skip_sb(struct session *ses, struct port_data *buddy, unsigned char *src, int srclen )
 {
 	int i;
@@ -1166,4 +1161,3 @@ int skip_sb(struct session *ses, struct port_data *buddy, unsigned char *src, in
 
 	return srclen + 1;
 }
-

+ 49 - 16
src/terminal.c

@@ -79,16 +79,27 @@ void init_terminal(struct session *ses)
 	{
 		syserr_fatal(-1, "init_terminal: tcgetattr 2");
 	}
+
+	print_stdout("\e=");
+	print_stdout("\e[>4;1m");
 }
 
 void reset_terminal(struct session *ses)
 {
-	if (tcsetattr(0, TCSANOW, &gtd->old_terminal))
+	if (gtd->detach_port == 0)
 	{
-		syserr_printf(ses, "reset_terminal: tcsetattr");
+		if (tcsetattr(0, TCSANOW, &gtd->old_terminal))
+		{
+			syserr_printf(ses, "reset_terminal: tcsetattr");
+		}
 	}
 
-	printf("\e[?1000l\e[?1002l\e[?1004l\e[?1006l");
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_MOUSETRACKING))
+	{
+		print_stdout("\e[?1000l\e[?1002l\e[?1004l\e[?1006l");
+	}
+	print_stdout("\e[23t");
+	print_stdout("\e[>4n");
 }
 
 
@@ -127,34 +138,51 @@ void echo_on(struct session *ses)
 void init_terminal_size(struct session *ses)
 {
 	struct winsize screen;
+	static int old_rows, old_cols;
 
 	push_call("init_terminal_size(%p)",ses);
 
 	if (ses == gts)
 	{
-		if (ioctl(0, TIOCGWINSZ, &screen) == -1)
-		{
-			init_screen(SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT * 16, SCREEN_WIDTH * 10);
-		}
-		else
+		old_rows = gtd->screen->rows;
+		old_cols = gtd->screen->cols;
+
+		if (ioctl(1, TIOCGWINSZ, &screen) >= 0)
 		{
 			init_screen(screen.ws_row, screen.ws_col, screen.ws_ypixel, screen.ws_xpixel);
+
+			if (gtd->attach_sock)
+			{
+				char buf[100];
+				sprintf(buf, "\e[8;%d;%dt\e[4;%d;%dt\e[7t", screen.ws_row, screen.ws_col, screen.ws_ypixel, screen.ws_xpixel);
+				write(gtd->attach_sock, buf, strlen(buf));
+			}
 		}
-		SET_BIT(gtd->flags, TINTIN_FLAG_RESETBUFFER);
 	}
 
-	if (HAS_BIT(ses->flags, SES_FLAG_SPLIT))
+	if (ses->scroll)
 	{
-		init_split(ses, 1 + ses->top_split, gtd->screen->rows - 1 - ses->bot_split);
+		SET_BIT(ses->scroll->flags, SCROLL_FLAG_RESIZE);
 	}
-	else
+
+	if (ses->map)
 	{
-		ses->top_row = 1;
-		ses->bot_row = gtd->screen->rows;
+		SET_BIT(ses->map->flags, MAP_FLAG_RESIZE);
 	}
 
+	init_split(ses, ses->split->sav_top_row, ses->split->sav_top_col, ses->split->sav_bot_row, ses->split->sav_bot_col);
+
 	check_all_events(ses, SUB_ARG, 0, 4, "SCREEN RESIZE", ntos(gtd->screen->rows), ntos(gtd->screen->cols), ntos(gtd->screen->height), ntos(gtd->screen->width));
 
+	if (old_rows <= old_cols / 2 && gtd->screen->rows > gtd->screen->cols / 2)
+	{
+		check_all_events(ses, SUB_ARG, 0, 4, "SCREEN ROTATE PORTRAIT", ntos(gtd->screen->rows), ntos(gtd->screen->cols), ntos(gtd->screen->height), ntos(gtd->screen->width));
+	}
+	else if (old_rows >= old_cols / 2 && gtd->screen->rows < gtd->screen->cols / 2)
+	{
+		check_all_events(ses, SUB_ARG, 0, 4, "SCREEN ROTATE LANDSCAPE", ntos(gtd->screen->rows), ntos(gtd->screen->cols), ntos(gtd->screen->height), ntos(gtd->screen->width));
+	}
+
 	msdp_update_all("SCREEN_ROWS",   "%d", gtd->screen->rows);
 	msdp_update_all("SCREEN_COLS",   "%d", gtd->screen->cols);
 	msdp_update_all("SCREEN_HEIGHT", "%d", gtd->screen->height);
@@ -164,7 +192,12 @@ void init_terminal_size(struct session *ses)
 	return;
 }
 
-int get_scroll_size(struct session *ses)
+int get_scroll_rows(struct session *ses)
+{
+	return (ses->split->bot_row - ses->split->top_row);
+}
+
+int get_scroll_cols(struct session *ses)
 {
-	return (ses->bot_row - ses->top_row);
+	return ses->wrap;
 }

+ 314 - 78
src/text.c

@@ -29,6 +29,7 @@
 
 void print_line(struct session *ses, char **str, int prompt)
 {
+	int height, width;
 	char *out;
 
 	push_call("print_line(%p,%p,%d)",ses,*str,prompt);
@@ -39,13 +40,21 @@ void print_line(struct session *ses, char **str, int prompt)
 		return;
 	}
 
-	if (HAS_BIT(ses->flags, SES_FLAG_SCAN) && gtd->verbose_level == 0)
+	if (HAS_BIT(ses->flags, SES_FLAG_SCAN) && gtd->level->verbose == 0)
 	{
 		pop_call();
 		return;
 	}
 
-	out = str_alloc(100 + strlen(*str) * 2 + 2);
+	if (HAS_BIT(ses->flags, SES_FLAG_SPLIT) && ses->wrap != gtd->screen->cols)
+	{
+		SET_BIT(ses->flags, SES_FLAG_PRINTLINE);
+
+		pop_call();
+		return;
+	}
+
+	out = str_alloc(BUFFER_SIZE + strlen(*str));
 
 	if (HAS_BIT(ses->flags, SES_FLAG_CONVERTMETA))
 	{
@@ -54,52 +63,93 @@ void print_line(struct session *ses, char **str, int prompt)
 		str_cpy(str, out);
 	}
 
-	if (ses->wrap)
+	if (HAS_BIT(ses->flags, SES_FLAG_SPLIT) || HAS_BIT(ses->flags, SES_FLAG_WORDWRAP))
 	{
-		word_wrap(ses, *str, out, TRUE);
+		word_wrap(ses, *str, out, TRUE, &height, &width);
 	}
 	else
 	{
-		strcpy(out, *str);
+		str_cpy(&out, *str);
 	}
 
 	if (prompt)
 	{
-		printf("%s", out);
+		print_stdout("%s", out);
 	}
 	else
 	{
-		printf("%s\n", out);
+		print_stdout("%s\n", out);
 	}
 	add_line_screen(out);
 
 	str_free(out);
 
+	SET_BIT(gtd->flags, TINTIN_FLAG_FLUSH);
+
 	pop_call();
 	return;
 }
 
+void print_stdout(char *format, ...)
+{
+	char *buffer;
+	va_list args;
+	int len;
+
+	va_start(args, format);
+	len = vasprintf(&buffer, format, args);
+	va_end(args);
+
+	if (gtd->detach_port)
+	{
+		if (gtd->detach_sock)
+		{
+			write(gtd->detach_sock, buffer, len);
+		}
+	}
+	else
+	{
+		write(STDIN_FILENO, buffer, len);
+	}
+	free(buffer);
+}
+
 /*
 	Word wrapper, only wraps scrolling region
 */
 
-int word_wrap(struct session *ses, char *textin, char *textout, int display)
+int word_wrap(struct session *ses, char *textin, char *textout, int flags, int *height, int *width)
 {
+	char color[COLOR_SIZE] = { 0 };
 	char *pti, *pto, *lis, *los, *chi, *cho;
-	int width, size, skip = 0, cnt = 0;
+	int cur_height, cur_width, size, skip, lines, cur_col, tab, wrap;
 
-	push_call("word_wrap(%s,%p,%p)",ses->name, textin,textout);
+	push_call("word_wrap(%s,%p,%p)",ses->name,textin,textout);
 
 	pti = chi = lis = textin;
 	pto = cho = los = textout;
 
+	cur_height   = 1;
+	lines        = 0;
+	*height      = 0;
+
+	cur_col      = ses->cur_col;
 	ses->cur_col = 1;
 
-	while (*pti != 0)
+	cur_width    = 0;
+	*width       = 0;
+
+	skip         = 0;
+
+	wrap = get_scroll_cols(ses);
+
+	while (*pti && pto - textout < BUFFER_SIZE)
 	{
 		if (skip_vt102_codes(pti))
 		{
-			if (display)
+			get_color_codes(color, pti, color, GET_ONE);
+
+			if (HAS_BIT(flags, WRAP_FLAG_DISPLAY))
 			{
 				interpret_vt102_codes(ses, pti, TRUE);
 			}
@@ -113,11 +163,25 @@ int word_wrap(struct session *ses, char *textin, char *textout, int display)
 
 		if (*pti == '\n')
 		{
+			lines++;
+			cur_height++;
+
 			*pto++ = *pti++;
-			cnt = cnt + 1;
-			los = pto;
+
 			lis = pti;
+			los = pto;
 
+			if (*pti)
+			{
+				pto += sprintf(pto, "%s", color);
+			}
+
+			if (cur_width > *width)
+			{
+				*width = cur_width;
+			}
+
+			cur_width    = 0;
 			ses->cur_col = 1;
 
 			continue;
@@ -129,16 +193,18 @@ int word_wrap(struct session *ses, char *textin, char *textout, int display)
 			lis = pti;
 		}
 
-		if (ses->cur_col > gtd->screen->cols || (ses->wrap > 0 && ses->cur_col > ses->wrap))
+		if (ses->cur_col > wrap)
 		{
-			cnt++;
 			ses->cur_col = 1;
+			cur_height++;
 
-			if (ses->wrap)
+			if (HAS_BIT(ses->flags, SES_FLAG_WORDWRAP))
 			{
-				if (pto - los > 15 || !SCROLL(ses))
+				if (pto - los >= 15 || wrap <= 20 || !SCROLL(ses))
 				{
 					*pto++ = '\n';
+					pto += sprintf(pto, "%s", color);
+
 					los = pto;
 					lis = pti;
 				}
@@ -146,6 +212,7 @@ int word_wrap(struct session *ses, char *textin, char *textout, int display)
 				{
 					pto = los;
 					*pto++ = '\n';
+					pto += sprintf(pto, "%s", color);
 					pti = chi = lis;
 					pti++;
 				}
@@ -157,67 +224,146 @@ int word_wrap(struct session *ses, char *textin, char *textout, int display)
 					pti++;
 				}
 			}
+			else if (ses->wrap)
+			{
+				*pto++ = '\n';
+			}
 		}
 		else
 		{
-			if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+			if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
 			{
 				*pto++ = *pti++;
 				*pto++ = *pti++;
 
-				ses->cur_col++;
+				cur_width += 2;
+				ses->cur_col += 2;
 			}
-			else if (HAS_BIT(ses->flags, SES_FLAG_UTF8) && is_utf8_head(pti))
+			else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
 			{
-				size = get_utf8_width(pti, &width);
+				size = get_utf8_width(pti, &tab);
 
 				while (size--)
 				{
 					*pto++ = *pti++;
 				}
-				ses->cur_col += width;
+				cur_width += tab;
+				ses->cur_col += tab;
+			}
+			else if (*pti == '\t')
+			{
+				tab = ses->tab_width - (ses->cur_col - 1) % ses->tab_width;
+
+				if (ses->cur_col + tab >= wrap) // xterm tabs
+				{
+					tab = (wrap - ses->cur_col);
+				}
+				pto += sprintf(pto, "%.*s", tab, "        ");
+				pti++;
+
+				cur_width += tab;
+				ses->cur_col += tab;
+
+				los = pto;
+				lis = pti;
 			}
 			else
 			{
 				*pto++ = *pti++;
+
+				cur_width++;
 				ses->cur_col++;
 			}
 		}
 	}
 	*pto = 0;
 
+	*height = cur_height + 1;
+
+	if (cur_width > *width)
+	{
+		*width = cur_width;
+	}
+
+	ses->cur_col = cur_col;
+
 	pop_call();
-	return (cnt + 1);
+	return lines + 1;
 }
 
 // store whatever falls inbetween skip and keep. Used by #buffer not checking SCROLL().
 
-int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap, int start, int end)
+int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap, int start, int end, int flags, int *height, int *width)
 {
-	char *pti, *pto, *lis, *los, *chi, *cho, *ptb;
-	int width, size, i = 0, lines = 0;
+	char color[COLOR_SIZE] = { 0 };
+	char *pti, *pto, *lis, *los;
+	int cur_height, size, i, lines, cur_col, cur_width, tab;
 
-	push_call("word_wrap_split(%s,%p,%p,%d,%d,%d)",ses->name,textin,textout,wrap,start,end);
+	push_call("word_wrap_split(%s,%p,%p,%d,%d,%d,%d)",ses->name,textin,textout,wrap,start,end,flags);
 
-	pti = chi = lis = textin;
-	pto = cho = los = textout;
+	pti = lis = textin;
+	pto = los = textout;
 
-	ses->cur_col = 1;
+	if (wrap <= 0)
+	{
+		wrap = ses->wrap;
+
+		if (ses->wrap == 0)
+		{
+			print_stdout("debug: word_wrap_split: wrap is 0\n");
+			pop_call();
+			return 1;
+		}
+	}
+
+	lines      = 0;
+	*height    = 0;
+	cur_height = 0;
+	cur_width  = 0;
+	*width     = 0;
+	cur_col    = 1;
 
-	while (*pti != 0)
+	if (HAS_BIT(flags, WRAP_FLAG_SPLIT) && end == 0)
 	{
-		if (skip_vt102_codes(pti))
+		print_stdout("debug: word_wrap_split: end point is 0.");
+	}
+
+	while (*pti && pto - textout < BUFFER_SIZE - 20)
+	{
+		push_call("skip");
+
+		if (cur_height > 10000 || cur_width > 10000)
+		{
+			print_stdout("debug: word_wrap_split: infinite loop?\n");
+			pop_call();
+			return 1;			
+		}
+		tab = skip_vt102_codes(pti);
+
+		if (tab)
 		{
-			for (i = skip_vt102_codes(pti) ; i > 0 ; i--)
+			get_color_codes(color, pti, color, GET_ONE);
+
+			for (i = 0 ; i < tab ; i++)
 			{
 				*pto++ = *pti++;
 			}
+			pop_call();
 			continue;
 		}
 
+		pop_call();
+		push_call("nl");
+
 		if (*pti == '\n')
 		{
-			if (lines++ >= start && lines < end)
+			lines++;
+			cur_height++;
+
+			lis = pti;
+			los = pto;
+
+			if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height > start && cur_height < end))
 			{
 				*pto++ = *pti++;
 			}
@@ -225,97 +371,187 @@ int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap,
 			{
 				pti++;
 			}
-			los = pto;
-			lis = pti;
 
-			ses->cur_col = 1;
+			if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height >= start && cur_height < end))
+			{
+				if (*pti)
+				{
+					pto += sprintf(pto, "%s", color);
+				}
+			}
 
+			if (cur_width > *width)
+			{
+				*width = cur_width;
+			}
+
+			cur_col = 1;
+			cur_width = 0;
+			pop_call();
 			continue;
 		}
 
-		if (*pti == ' ')
+		if (*pti == ' ' || *pti == '\t')
 		{
-			los = pto;
 			lis = pti;
+			los = pto;
 		}
 
-		if (ses->cur_col > gtd->screen->cols || (wrap > 0 && ses->cur_col > wrap))
+		pop_call();
+		push_call("wrap");
+
+		if (cur_col > wrap)
 		{
-			lines++;
-			ses->cur_col = 1;
+			cur_col = 1;
+
+			cur_height++;
 
-			if (wrap)
+			if (HAS_BIT(ses->flags, SES_FLAG_WORDWRAP))
 			{
-				if (pto - los > 15 || ses->cur_col < 15)
+				if (pto - los > 15 || wrap <= 20)
 				{
-					if (lines >= start && lines < end)
-					{
-						*pto++ = '\n';
-					}
 					los = pto;
 					lis = pti;
-				}
-				else if (lis != chi) // infinite VT loop detection
-				{
-					if (lines >= start && lines < end)
+
+					if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height > start && cur_height < end))
 					{
-						pto = los;
 						*pto++ = '\n';
 					}
-					pti = chi = lis;
-					pti++;
+
+					if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height >= start && cur_height < end))
+					{
+						pto += sprintf(pto, "%s", color);
+					}
 				}
-				else if (los != cho)
+				else
 				{
-					if (lines >= start && lines < end)
+					pti = lis;
+					pto = los;
+
+					if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height > start && cur_height < end))
 					{
-						pto = cho = los;
-						pto++;
+						*pto++ = '\n';
 					}
-					else
+
+					if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height >= start && cur_height < end))
 					{
-						cho = los;
+						pto += sprintf(pto, "%s", color);
+
 					}
-					pti = chi = lis;
-					pti++;
 				}
 			}
+			else
+			{
+				los = pto;
+				lis = pti;
+
+				if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height > start && cur_height < end))
+				{
+					*pto++ = '\n';
+				}
+
+				if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height >= start && cur_height < end))
+				{
+					pto += sprintf(pto, "%s", color);
+				}
+			}
+			pop_call();
+			continue;
 		}
-		else
-		{
-			ptb = pto;
 
-			if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && is_big5(pti))
+		pop_call();
+		push_call("default");
+
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && is_big5(pti))
+		{
+			if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height >= start && cur_height < end))
 			{
 				*pto++ = *pti++;
 				*pto++ = *pti++;
-				ses->cur_col++;
 			}
-			else if (HAS_BIT(ses->flags, SES_FLAG_UTF8) && is_utf8_head(pti))
+			else
 			{
-				size = get_utf8_width(pti, &width);
+				pti += 2;
+			}
+			cur_width += 2;
+			cur_col += 2;
+		}
+		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
+		{
+			size = get_utf8_width(pti, &tab);
 
-				while (size--)
+			if (size)
+			{
+				if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height >= start && cur_height < end))
 				{
-					*pto++ = *pti++;
+					while (size--)
+					{
+						*pto++ = *pti++;
+					}
+				}
+				else
+				{
+					pti += size;
 				}
-				ses->cur_col += width;
+				cur_width += tab;
+				cur_col += tab;
 			}
 			else
 			{
+				print_stdout("debug: word_wrap_split: utf8 error\n");
 				*pto++ = *pti++;
-				ses->cur_col++;
+				cur_width++;
+				cur_col++;
 			}
+		}
+		else
+		{
+			if (*pti == '\t')
+			{
+				los = pto;
+				lis = pti;
+
+				tab = ses->tab_width - (ses->cur_col - 1) % ses->tab_width;
+
+				if (cur_col + tab >= wrap)
+				{
+					tab = (wrap - cur_col);
+				}
 
-			if (lines < start || lines >= end)
+				if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height >= start && cur_height < end))
+				{
+					pto += sprintf(pto, "%.*s", tab, "        ");
+				}
+				pti++;
+
+				cur_width += tab;
+				cur_col += tab;
+			}
+			else
 			{
-				pto = ptb;
+				if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height >= start && cur_height < end))
+				{
+					*pto++ = *pti++;
+				}
+				else
+				{
+					pti++;
+				}
+				cur_width++;
+				cur_col++;
 			}
 		}
+		pop_call();
 	}
 	*pto = 0;
 
+	if (cur_width > *width)
+	{
+		*width = cur_width;
+	}
+	*height = cur_height + 1;
+
 	pop_call();
-	return (lines + 1);
+	return lines + 1;
 }
 

Різницю між файлами не показано, бо вона завелика
+ 467 - 215
src/tintin.h


+ 6 - 11
src/tokenize.c

@@ -79,7 +79,7 @@ void debugtoken(struct session *ses, struct scriptroot *root, struct scriptnode
 {
 	push_call("debugtoken(%p,%d,%p,%d)",ses,root->list,token,token->type);
 
-	if (gtd->debug_level)
+	if (gtd->level->debug || HAS_BIT(root->ses->list[root->list]->flags, LIST_FLAG_DEBUG) || HAS_BIT(root->ses->list[root->list]->flags, LIST_FLAG_LOG))
 	{
 		switch (token->type)
 		{
@@ -349,11 +349,11 @@ char *get_arg_parse(struct session *ses, struct scriptnode *token)
 {
 	static char buf[5];
 
-	if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && token->data->arg[0] & 128 && token->data->arg[1] != 0)
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && token->data->arg[0] & 128 && token->data->arg[1] != 0)
 	{
 		token->data->arg += sprintf(buf, "%c%c", token->data->arg[0], token->data->arg[1]);
 	}
-	else if (HAS_BIT(ses->flags, SES_FLAG_UTF8) && is_utf8_head(token->data->arg))
+	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(token->data->arg))
 	{
 		token->data->arg += sprintf(buf, "%.*s", get_utf8_size(token->data->arg), token->data->arg);
 	}
@@ -924,7 +924,7 @@ struct scriptnode *parse_script(struct scriptroot *root, int lvl, struct scriptn
 			case TOKEN_TYPE_REGEX:
 				split = NULL;
 
-				token->regex->val = find(root->ses, token->str, token->regex->str, SUB_VAR|SUB_FUN, SUB_CMD);
+				token->regex->val = find(root->ses, token->str, token->regex->str, SUB_VAR|SUB_FUN, REGEX_FLAG_CMD);
 
 				if (token->regex->val)
 				{
@@ -1172,7 +1172,6 @@ char *view_script(struct session *ses, struct scriptroot *root)
 struct session *script_driver(struct session *ses, int list, char *str)
 {
 	struct scriptroot *root;
-	int debug;
 
 	push_call("script_driver(%p,%d,%p)",ses,list,str);
 
@@ -1182,10 +1181,7 @@ struct session *script_driver(struct session *ses, int list, char *str)
 	root->list = list;
 	root->local = init_list(ses, LIST_VARIABLE, LIST_SIZE);
 
-	debug = HAS_BIT(ses->list[list]->flags, LIST_FLAG_DEBUG);
-
-	gtd->debug_level += debug;
-	gtd->input_level += list != LIST_COMMAND;
+	gtd->level->input += list != LIST_COMMAND;
 
 	script_stack[++script_index] = root;
 
@@ -1195,8 +1191,7 @@ struct session *script_driver(struct session *ses, int list, char *str)
 
 	script_index--;
 
-	gtd->debug_level -= debug;
-	gtd->input_level -= list != LIST_COMMAND;
+	gtd->level->input -= list != LIST_COMMAND;
 
 	while (root->prev)
 	{

+ 74 - 54
src/trigger.c

@@ -35,11 +35,6 @@ DO_COMMAND(do_action)
 	arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
 	arg = get_arg_in_braces(ses, arg, arg3, GET_ALL);
 
-	if (*arg3 == 0)
-	{
-//		strcpy(arg3, "5");
-	}
-
 	if (*arg1 == 0)
 	{
 		show_list(ses->list[LIST_ACTION], 0);
@@ -72,15 +67,23 @@ DO_COMMAND(do_unaction)
 void check_all_actions(struct session *ses, char *original, char *line)
 {
 	struct listroot *root = ses->list[LIST_ACTION];
+	struct listnode *node;
 	char buf[BUFFER_SIZE];
 
 	for (root->update = 0 ; root->update < root->used ; root->update++)
 	{
-		if (check_one_regexp(ses, root->list[root->update], line, original, 0))
+		node = root->list[root->update];
+
+		if (check_one_regexp(ses, node, line, original, 0))
 		{
-			show_debug(ses, LIST_ACTION, "#DEBUG ACTION {%s}", root->list[root->update]->arg1);
+			show_debug(ses, LIST_ACTION, "#DEBUG ACTION {%s}", node->arg1);
+
+			substitute(ses, node->arg2, buf, SUB_ARG|SUB_SEC);
 
-			substitute(ses, root->list[root->update]->arg2, buf, SUB_ARG|SUB_SEC);
+			if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+			{
+				delete_node_list(ses, LIST_ACTION, node);
+			}
 
 			script_driver(ses, LIST_ACTION, buf);
 
@@ -104,11 +107,6 @@ DO_COMMAND(do_alias)
 	arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
 	arg = get_arg_in_braces(ses, arg, arg3, GET_ALL);
 
-	if (*arg3 == 0)
-	{
-//		strcpy(arg3, "5");
-	}
-
 	if (*arg1 == 0)
 	{
 		show_list(ses->list[LIST_ALIAS], 0);
@@ -155,10 +153,10 @@ int check_all_aliases(struct session *ses, char *input)
 
 	for (root->update = 0 ; root->update < root->used ; root->update++)
 	{
-		if (check_one_regexp(ses, root->list[root->update], line, line, PCRE_ANCHORED))
-		{
-			node = root->list[root->update];
+		node = root->list[root->update];
 
+		if (check_one_regexp(ses, node, line, line, PCRE_ANCHORED))
+		{
 			i = strlen(node->arg1);
 
 			if (!strncmp(node->arg1, line, i))
@@ -212,6 +210,10 @@ int check_all_aliases(struct session *ses, char *input)
 
 			show_debug(ses, LIST_ALIAS, "#DEBUG ALIAS {%s} {%s}", node->arg1, gtd->vars[0]);
 
+			if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+			{
+				delete_node_list(ses, LIST_ALIAS, node);
+			}
 			return TRUE;
 		}
 	}
@@ -249,10 +251,6 @@ DO_COMMAND(do_button)
 	}
 	else
 	{
-		if (*arg3 == 0)
-		{
-//			strcpy(arg3, "5");
-		}
 		node = update_node_list(ses->list[LIST_BUTTON], arg1, arg2, arg3, "");
 
 		show_message(ses, LIST_BUTTON, "#OK. BUTTON {%s} NOW TRIGGERS {%s} @ {%s}.", arg1, arg2, arg3);
@@ -313,7 +311,7 @@ void check_all_buttons(struct session *ses, short row, short col, char *arg1, ch
 
 	for (root->update = 0 ; root->update < root->used ; root->update++)
 	{
-		node = root->list[root->update];
+		node = root->list[root->update];;
 
 		val[0] = node->val16[0] < 0 ? 1 + gtd->screen->rows + node->val16[0] : node->val16[0];
 		val[1] = node->val16[1] < 0 ? 1 + gtd->screen->cols + node->val16[1] : node->val16[1];
@@ -343,7 +341,11 @@ void check_all_buttons(struct session *ses, short row, short col, char *arg1, ch
 			RESTRING(gtd->vars[5], line);
 
 			substitute(ses, node->arg2, buf, SUB_ARG|SUB_SEC);
-			
+
+			if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+			{
+				delete_node_list(ses, LIST_BUTTON, node);
+			}
 			script_driver(ses, LIST_BUTTON, buf);
 
 			return;
@@ -360,7 +362,7 @@ void check_all_buttons(struct session *ses, short row, short col, char *arg1, ch
 
 DO_COMMAND(do_delay)
 {
-	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE], temp[BUFFER_SIZE];
+	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE], time[BUFFER_SIZE];
 
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 	arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
@@ -383,19 +385,23 @@ DO_COMMAND(do_delay)
 		{
 			sprintf(arg3, "%lld", utime() + (long long) (1000000 * get_number(ses, arg1)));
 
-			get_number_string(ses, arg1, temp);
+			get_number_string(ses, arg1, time);
 
-			update_node_list(ses->list[LIST_DELAY], arg3, arg2, temp, "");
+			update_node_list(ses->list[LIST_DELAY], arg3, arg2, time, "");
 
-			show_message(ses, LIST_TICKER, "#OK, IN {%s} SECONDS {%s} IS EXECUTED.", temp, arg2);
+			show_message(ses, LIST_DELAY, "#OK, IN {%s} SECONDS {%s} IS EXECUTED.", time, arg2);
 		}
 		else
 		{
-			get_number_string(ses, arg3, temp);
+			get_number_string(ses, arg3, time);
+
+			gtd->level->oneshot++;
 
-			update_node_list(ses->list[LIST_DELAY], arg1, arg2, temp, "");
+			update_node_list(ses->list[LIST_TICKER], arg1, arg2, time, "");
 
-			show_message(ses, LIST_TICKER, "#OK, IN {%s} SECONDS {%s} IS EXECUTED.", temp, arg2);
+			gtd->level->oneshot--;
+			
+			show_message(ses, LIST_TICKER, "#OK. #TICK {%s} WILL EXECUTE {%s} IN {%s} SECONDS.", arg1, arg2, time);
 		}
 	}
 	return ses;
@@ -403,7 +409,14 @@ DO_COMMAND(do_delay)
 
 DO_COMMAND(do_undelay)
 {
-	delete_node_with_wild(ses, LIST_DELAY, arg);
+	if (isalpha(*arg))
+	{
+		delete_node_with_wild(ses, LIST_TICKER, arg);
+	}
+	else
+	{
+		delete_node_with_wild(ses, LIST_DELAY, arg);
+	}
 
 	return ses;
 }
@@ -493,13 +506,20 @@ DO_COMMAND(do_ungag)
 void check_all_gags(struct session *ses, char *original, char *line)
 {
 	struct listroot *root = ses->list[LIST_GAG];
+	struct listnode *node;
 
 	for (root->update = 0 ; root->update < root->used ; root->update++)
 	{
-		if (check_one_regexp(ses, root->list[root->update], line, original, 0))
+		node = root->list[root->update];
+
+		if (check_one_regexp(ses, node, line, original, 0))
 		{
-			show_debug(ses, LIST_GAG, "#DEBUG GAG {%s}", root->list[root->update]->arg1);
+			show_debug(ses, LIST_GAG, "#DEBUG GAG {%s}", node->arg1);
 
+			if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+			{
+				delete_node_list(ses, LIST_GAG, node);
+			}
 			SET_BIT(ses->flags, SES_FLAG_GAG);
 
 			return;
@@ -524,11 +544,6 @@ DO_COMMAND(do_highlight)
 	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
 	arg = get_arg_in_braces(ses, arg, arg3, GET_ALL);
 
-	if (*arg3 == 0)
-	{
-//		strcpy(arg3, "5");
-	}
-
 	if (*arg1 == 0)
 	{
 		show_list(ses->list[LIST_HIGHLIGHT], 0);
@@ -577,10 +592,10 @@ void check_all_highlights(struct session *ses, char *original, char *line)
 
 	for (root->update = 0 ; root->update < root->used ; root->update++)
 	{
-		if (check_one_regexp(ses, root->list[root->update], line, original, 0))
-		{
-			node = root->list[root->update];
+		node = root->list[root->update];
 
+		if (check_one_regexp(ses, node, line, original, 0))
+		{
 			get_color_names(ses, node->arg2, color);
 
 			*output = *reset = 0;
@@ -614,7 +629,7 @@ void check_all_highlights(struct session *ses, char *original, char *line)
 
 				*ptm = 0;
 
-				get_color_codes(reset, pto, reset);
+				get_color_codes(reset, pto, reset, GET_ALL);
 
 				cat_sprintf(output, "%s%s%s\e[0m%s", pto, color, plain, reset);
 
@@ -624,6 +639,10 @@ void check_all_highlights(struct session *ses, char *original, char *line)
 			}
 			while (check_one_regexp(ses, node, ptl, pto, 0));
 
+			if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+			{
+				delete_node_list(ses, LIST_HIGHLIGHT, node);
+			}
 			strcat(output, pto);
 
 			strcpy(original, output);
@@ -740,10 +759,10 @@ void check_all_prompts(struct session *ses, char *original, char *line)
 
 	for (root->update = 0 ; root->update < root->used ; root->update++)
 	{
-		if (check_one_regexp(ses, root->list[root->update], line, original, 0))
-		{
-			node = root->list[root->update];
+		node = root->list[root->update];
 
+		if (check_one_regexp(ses, node, line, original, 0))
+		{
 			if (*node->arg2)
 			{
 				substitute(ses, node->arg2, original, SUB_ARG);
@@ -755,6 +774,10 @@ void check_all_prompts(struct session *ses, char *original, char *line)
 
 			split_show(ses, original, atoi(node->arg3), atoi(node->arg4));
 
+			if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+			{
+				delete_node_list(ses, LIST_GAG, node);
+			}
 			SET_BIT(ses->flags, SES_FLAG_GAG);
 		}
 	}
@@ -777,11 +800,6 @@ DO_COMMAND(do_substitute)
 	arg = get_arg_in_braces(ses, str, arg2, GET_ALL);
 	arg = get_arg_in_braces(ses, arg, arg3, GET_ALL);
 
-	if (*arg3 == 0)
-	{
-//		strcpy(arg3, "5");
-	}
-
 	if (*arg1 == 0)
 	{
 		show_list(ses->list[LIST_SUBSTITUTE], 0);
@@ -819,10 +837,10 @@ void check_all_substitutions(struct session *ses, char *original, char *line)
 
 	for (root->update = 0 ; root->update < root->used ; root->update++)
 	{
-		if (check_one_regexp(ses, root->list[root->update], line, original, 0))
-		{
-			node = root->list[root->update];
+		node = root->list[root->update];
 
+		if (check_one_regexp(ses, node, line, original, 0))
+		{
 			pto = original;
 			ptl = line;
 
@@ -863,10 +881,12 @@ void check_all_substitutions(struct session *ses, char *original, char *line)
 			}
 			while (check_one_regexp(ses, node, ptl, pto, 0));
 
+			if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+			{
+				delete_node_list(ses, LIST_SUBSTITUTE, node);
+			}
 			strcat(output, pto);
 
-//			substitute(ses, output, original, SUB_VAR|SUB_FUN|SUB_COL|SUB_ESC);
-
 			strcpy(original, output);
 
 			strip_vt102_codes(original, line);

+ 489 - 158
src/update.c

@@ -28,134 +28,157 @@
 
 #include <sys/types.h>
 #include <sys/time.h>
+#include <fcntl.h>
 #include <termios.h>
-
+#include <sys/un.h>
+
+extern void update_input(void);
+extern void update_sessions(void);
+extern void update_daemon(void);
+extern void update_chat(void);
+extern void update_port(void);
+extern void tick_update(void);
+extern void delay_update(void);
+extern void path_update(void);
+extern void packet_update(void);
+extern void terminal_update(void);
+extern void memory_update(void);
+extern void time_update(void);
+
+extern long long display_timer(struct session *ses, int timer);
+extern void open_timer(int timer);
+extern void close_timer(int timer);
 
 void mainloop(void)
 {
-	static struct timeval curr_time, wait_time, last_time;
-	int usec_loop, usec_wait;
-
-	short int pulse_poll_input      = 0 + PULSE_POLL_INPUT;
-	short int pulse_poll_sessions   = 0 + PULSE_POLL_SESSIONS;
-	short int pulse_poll_chat       = 0 + PULSE_POLL_CHAT;
-	short int pulse_poll_port       = 0 + PULSE_POLL_PORT;
-	short int pulse_update_ticks    = 0 + PULSE_UPDATE_TICKS;
-	short int pulse_update_delays   = 0 + PULSE_UPDATE_DELAYS;
-	short int pulse_update_packets  = 0 + PULSE_UPDATE_PACKETS;
-	short int pulse_update_chat     = 0 + PULSE_UPDATE_CHAT;
-	short int pulse_update_terminal = 0 + PULSE_UPDATE_TERMINAL;
-	short int pulse_update_memory   = 0 + PULSE_UPDATE_MEMORY;
-	short int pulse_update_time     = 1 + PULSE_UPDATE_TIME;
-
-	wait_time.tv_sec = 0;
+	static struct timeval start_time, end_time, span_time, wait_time;
+	static struct pulse_type pulse;
+
+	pulse.update_input    =  0 + PULSE_UPDATE_INPUT;
+	pulse.update_sessions =  0 + PULSE_UPDATE_SESSIONS;
+	pulse.update_delays   =  0 + PULSE_UPDATE_DELAYS;
+	pulse.update_daemon   =  0 + PULSE_UPDATE_DAEMON;
+	pulse.update_chat     =  2 + PULSE_UPDATE_CHAT;
+	pulse.update_port     =  2 + PULSE_UPDATE_PORT;
+	pulse.update_ticks    =  3 + PULSE_UPDATE_TICKS;
+	pulse.update_paths    =  3 + PULSE_UPDATE_PATHS;
+	pulse.update_packets  =  4 + PULSE_UPDATE_PACKETS;
+	pulse.update_terminal =  6 + PULSE_UPDATE_TERMINAL;
+	pulse.update_memory   =  7 + PULSE_UPDATE_MEMORY;
+	pulse.update_time     =  8 + PULSE_UPDATE_TIME;
 
 	push_call("mainloop()");
 
 	while (TRUE)
 	{
-		gettimeofday(&last_time, NULL);
+		gettimeofday(&start_time, NULL);
+
+		gtd->total_io_exec  += span_time.tv_usec;
+		gtd->total_io_delay += wait_time.tv_usec;
+
+		if (--pulse.update_delays == 0)
+		{
+			pulse.update_delays = PULSE_UPDATE_DELAYS;
 
-		if (--pulse_poll_input == 0)
+			delay_update();
+		}
+
+		if (--pulse.update_input == 0)
 		{
-			open_timer(TIMER_POLL_INPUT);
+			open_timer(TIMER_UPDATE_INPUT);
 
-			pulse_poll_input = PULSE_POLL_INPUT;
+			pulse.update_input = PULSE_UPDATE_INPUT;
 
-			poll_input();
+			update_input();
 
-			close_timer(TIMER_POLL_INPUT);
+			close_timer(TIMER_UPDATE_INPUT);
 		}
 
-		if (--pulse_poll_sessions == 0)
+		if (--pulse.update_sessions == 0)
 		{
-			pulse_poll_sessions = PULSE_POLL_SESSIONS;
+			pulse.update_sessions = PULSE_UPDATE_SESSIONS;
 
-			poll_sessions();
+			update_sessions();
 		}
 
-		if (--pulse_poll_chat == 0)
+		if (--pulse.update_daemon == 0)
 		{
-			pulse_poll_chat = PULSE_POLL_CHAT;
+			pulse.update_daemon = PULSE_UPDATE_DAEMON;
 
-			poll_chat();
-		}	
+			update_daemon();
+		}
 
-		if (--pulse_poll_port == 0)
+		if (--pulse.update_chat == 0)
 		{
-			pulse_poll_port = PULSE_POLL_PORT;
+			pulse.update_chat = PULSE_UPDATE_CHAT;
 
-			poll_port();
+			update_chat();
 		}	
 
-		if (--pulse_update_ticks == 0)
+		if (--pulse.update_port == 0)
 		{
-			pulse_update_ticks = PULSE_UPDATE_TICKS;
+			pulse.update_port = PULSE_UPDATE_PORT;
 
-			tick_update();
-		}
+			update_port();
+		}	
 
-		if (--pulse_update_delays == 0)
+		if (--pulse.update_ticks == 0)
 		{
-			pulse_update_delays = PULSE_UPDATE_DELAYS;
+			pulse.update_ticks = PULSE_UPDATE_TICKS;
 
-			delay_update();
+			tick_update();
 		}
 
-		if (--pulse_update_packets == 0)
+		if (--pulse.update_paths == 0)
 		{
-			pulse_update_packets = PULSE_UPDATE_PACKETS;
+			pulse.update_paths = PULSE_UPDATE_PATHS;
 
-			packet_update();
+			path_update();
 		}
 
-		if (--pulse_update_chat == 0)
+
+		if (--pulse.update_packets == 0)
 		{
-			pulse_update_chat = PULSE_UPDATE_CHAT;
+			pulse.update_packets = PULSE_UPDATE_PACKETS;
 
-			chat_update();
+			packet_update();
 		}
 
-		if (--pulse_update_terminal == 0)
+		if (--pulse.update_terminal == 0)
 		{
-			pulse_update_terminal = PULSE_UPDATE_TERMINAL;
+			pulse.update_terminal = PULSE_UPDATE_TERMINAL;
 
 			terminal_update();
 		}
 
-		if (--pulse_update_memory == 0)
+		if (--pulse.update_memory == 0)
 		{
-			pulse_update_memory = PULSE_UPDATE_MEMORY;
+			pulse.update_memory = PULSE_UPDATE_MEMORY;
 
 			memory_update();
 		}
 
-		if (--pulse_update_time == 0)
+		if (--pulse.update_time == 0)
 		{
-			pulse_update_time = PULSE_UPDATE_TIME;
+			pulse.update_time = PULSE_UPDATE_TIME;
 
 			time_update();
 		}
 
-		gettimeofday(&curr_time, NULL);
+		gettimeofday(&end_time, NULL);
 
-		if (curr_time.tv_sec == last_time.tv_sec)
+		if (start_time.tv_sec == end_time.tv_sec)
 		{
-			usec_loop = curr_time.tv_usec - last_time.tv_usec;
+			span_time.tv_usec = end_time.tv_usec - start_time.tv_usec;
 		}
 		else
 		{
-			usec_loop = 1000000 - last_time.tv_usec + curr_time.tv_usec;
+			span_time.tv_usec = (end_time.tv_sec * 1000000LL + end_time.tv_usec) - (start_time.tv_sec * 1000000LL + start_time.tv_usec);
 		}
 
-		usec_wait = 1000000 / PULSE_PER_SECOND - usec_loop;
-
-		wait_time.tv_usec = usec_wait;
+		wait_time.tv_usec = 1000000 / PULSE_PER_SECOND - span_time.tv_usec;
 
-		gtd->total_io_exec  += usec_loop;
-		gtd->total_io_delay += usec_wait;
-
-		if (usec_wait > 0)
+		if (wait_time.tv_usec > 0)
 		{
 			select(0, NULL, NULL, NULL, &wait_time);
 		}
@@ -164,48 +187,81 @@ void mainloop(void)
 	return;
 }
 
-void poll_input(void)
+void update_input(void)
 {
-	fd_set readfds;
-	static struct timeval to;
+	fd_set read_fd;
+	static struct timeval timeout;
+
+	if (gtd->detach_port)
+	{
+		return;
+
+		if (gtd->detach_sock)
+		{
+			while (TRUE)
+			{
+				FD_ZERO(&read_fd);
+
+				FD_SET(gtd->detach_sock, &read_fd);
+			
+				if (select(FD_SETSIZE, &read_fd, NULL, NULL, &timeout) <= 0)
+				{
+					break;
+				}
+
+				if (!FD_ISSET(gtd->detach_sock, &read_fd))
+				{
+					break;
+				}
+//				process_input();
+			}
+		}
+		return;
+	}
 
 	while (TRUE)
 	{
-		FD_ZERO(&readfds);
+		FD_ZERO(&read_fd);
 
-		FD_SET(0, &readfds);
+		FD_SET(STDIN_FILENO, &read_fd);
 
-		if (select(FD_SETSIZE, &readfds, NULL, NULL, &to) <= 0)
+		if (select(FD_SETSIZE, &read_fd, NULL, NULL, &timeout) <= 0)
 		{
-			return;
+			break;
 		}
 
-		if (FD_ISSET(0, &readfds))
+		if (!FD_ISSET(STDIN_FILENO, &read_fd))
 		{
-			process_input();
+			break;
 		}
-		else
+
+		process_input();
+
+		SET_BIT(gtd->flags, TINTIN_FLAG_FLUSH);
+
+		if (gtd->detach_port)
 		{
 			return;
 		}
 	}
+	return;
 }
 
-void poll_sessions(void)
+void update_sessions(void)
 {
-	fd_set readfds, excfds;
-	static struct timeval to;
+	fd_set read_fd, error_fd;
+	static struct timeval timeout;
 	struct session *ses;
 	int rv;
 
-	push_call("poll_sessions(void)");
+	push_call("update_sessions(void)");
 
-	open_timer(TIMER_POLL_SESSIONS);
+	open_timer(TIMER_UPDATE_SESSIONS);
 
 	if (gts->next)
 	{
-		FD_ZERO(&readfds);
-		FD_ZERO(&excfds);
+		FD_ZERO(&read_fd);
+		FD_ZERO(&error_fd);
 
 		for (ses = gts->next ; ses ; ses = gtd->update)
 		{
@@ -215,17 +271,16 @@ void poll_sessions(void)
 			{
 				while (TRUE)
 				{
-					FD_SET(ses->socket, &readfds);
-					FD_SET(ses->socket, &excfds);
+					FD_SET(ses->socket, &read_fd);
+					FD_SET(ses->socket, &error_fd);
 
-					rv = select(FD_SETSIZE, &readfds, NULL, &excfds, &to);
+					rv = select(FD_SETSIZE, &read_fd, NULL, &error_fd, &timeout);
 
 					if (rv < 0)
 					{
 						break;
 
-//						ses->
-						syserr_printf(ses, "poll_sessions: select = %d:", rv);
+						syserr_printf(ses, "update_sessions: %s:", ses->name);
 
 						cleanup_session(ses);
 
@@ -239,7 +294,7 @@ void poll_sessions(void)
 						break;
 					}
 
-					if (FD_ISSET(ses->socket, &readfds))
+					if (FD_ISSET(ses->socket, &read_fd))
 					{
 						if (read_buffer_mud(ses) == FALSE)
 						{
@@ -253,9 +308,9 @@ void poll_sessions(void)
 						}
 					}
 
-					if (FD_ISSET(ses->socket, &excfds))
+					if (FD_ISSET(ses->socket, &error_fd))
 					{
-						FD_CLR(ses->socket, &readfds);
+						FD_CLR(ses->socket, &read_fd);
 
 						cleanup_session(ses);
 
@@ -272,63 +327,317 @@ void poll_sessions(void)
 			}
 		}
 	}
-	close_timer(TIMER_POLL_SESSIONS);
+
+	for (ses = gts ; ses ; ses = gtd->update)
+	{
+		gtd->update = ses->next;
+
+		if (ses->check_output == 0 && HAS_BIT(ses->flags, SES_FLAG_PRINTLINE))
+		{
+			DEL_BIT(ses->flags, SES_FLAG_PRINTLINE);
+			SET_BIT(ses->flags, SES_FLAG_PRINTBUFFER);
+
+			buffer_end(ses, "");
+
+			DEL_BIT(ses->flags, SES_FLAG_PRINTBUFFER);
+		}
+	}
+
+
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_FLUSH))
+	{
+		DEL_BIT(gtd->flags, TINTIN_FLAG_FLUSH);
+
+//		if (gtd->detach_port == 0)
+		{
+			fflush(stdout);
+		}
+	}
+
+	close_timer(TIMER_UPDATE_SESSIONS);
 
 	pop_call();
 	return;
 }
 
-void poll_chat(void)
+void update_daemon(void)
+{
+	fd_set read_fd, error_fd;
+	static struct timeval timeout;
+	socklen_t len;
+	int rv;
+
+	if (gtd->detach_port)
+	{
+		if (TRUE)
+		{
+			FD_ZERO(&read_fd);
+
+			FD_SET(gtd->detach_port, &read_fd);
+
+			rv = select(FD_SETSIZE, &read_fd, NULL, NULL, &timeout);
+
+			if (rv > 0)
+			{
+				if (FD_ISSET(gtd->detach_port, &read_fd))
+				{
+					if (gtd->detach_sock)
+					{
+						tintin_printf2(gtd->ses, "#DAEMON UPDATE: ANOTHER CONNECTION IS TAKING OVER {%s}.", gtd->detach_file);
+						kill((pid_t) gtd->detach_sock, SIGTSTP);
+						close(gtd->detach_sock);
+					}
+
+					gtd->detach_sock = accept(gtd->detach_port, 0, 0);
+
+					if (gtd->detach_sock < 0)
+					{
+						syserr_printf(gtd->ses, "update_daemon: detach_port: accept");
+						
+						gtd->detach_sock = close(gtd->detach_sock);
+						
+						goto attach;
+					}
+
+					if (fcntl(gtd->detach_sock, F_SETFL, O_NDELAY|O_NONBLOCK) == -1)
+					{
+						syserr_printf(gtd->ses, "update_daemon: detach_port: fcntl O_NDELAY|O_NONBLOCK");
+
+						gtd->detach_sock = close(gtd->detach_sock);
+						
+						goto attach;
+					}
+
+					len = sizeof(struct process_data);
+
+					if (getsockopt(gtd->detach_sock, SOL_SOCKET, SO_PEERCRED, &gtd->detach_info, &len) == -1)
+					{
+						syserr_printf(gtd->ses, "update_daemon: getsockopt:");
+
+						gtd->detach_sock = close(gtd->detach_sock);
+
+						goto attach;
+					}
+
+					if (geteuid() != gtd->detach_info.uid)
+					{
+						tintin_printf2(gtd->ses, "#DAEMON UPDATE: YOUR UID IS %d WHILE {%s} HAS UID {%d}.", geteuid(), gtd->detach_file, gtd->detach_info.uid);
+
+						gtd->detach_sock = close(gtd->detach_sock);
+
+						goto attach;
+					}
+
+//					tintin_printf2(gtd->ses, "sock=%d pid=%d, euid=%d, egid=%d", gtd->detach_port, getpid(), geteuid(), getegid());
+//					tintin_printf2(gtd->ses, "sock=%d pid=%d, euid=%d, egid=%d", gtd->detach_sock, gtd->detach_info.pid, gtd->detach_info.uid, gtd->detach_info.gid);
+
+					winch_handler(0);
+
+					dirty_screen(gtd->ses);
+
+					tintin_printf2(gtd->ses, "#DAEMON UPDATE: ATTACHED {%s} TO PID {%d}.", gtd->detach_file, gtd->detach_info.pid);
+				}
+			}
+			else if (rv < 0)
+			{
+				if (errno != EINTR)
+				{
+					syserr_printf(gtd->ses, "update_daemon: select:");
+				}
+			}
+		}
+
+		if (gtd->detach_sock > 0)
+		{
+			while (gtd->detach_sock)
+			{
+				FD_ZERO(&read_fd);
+				FD_ZERO(&error_fd);
+
+				FD_SET(gtd->detach_sock, &read_fd);
+				FD_SET(gtd->detach_sock, &error_fd);
+
+				rv = select(FD_SETSIZE, &read_fd, NULL, &error_fd, &timeout);
+
+				if (rv == 0)
+				{
+					break;
+				}
+				else if (rv < 0)
+				{
+					FD_CLR(gtd->detach_sock, &read_fd);
+
+					gtd->detach_sock = close(gtd->detach_sock);
+
+	                                syserr_printf(gtd->ses, "update_daemon: detach_sock: select:");
+
+	                                break;
+				}
+				else if (rv > 0)
+				{
+					if (FD_ISSET(gtd->detach_sock, &error_fd))
+					{
+						FD_CLR(gtd->detach_sock, &read_fd);
+
+						gtd->detach_sock = close(gtd->detach_sock);
+
+						show_error(gtd->ses, LIST_COMMAND, "update_daemon: detach_sock: error_fd");
+
+						goto attach;
+					}
+
+					if (!FD_ISSET(gtd->detach_sock, &read_fd))
+					{
+						break;
+					}
+					process_input();
+				}
+			}
+		}
+	}
+
+	attach:
+
+	if (gtd->attach_sock)
+	{
+		FD_ZERO(&read_fd);
+		FD_ZERO(&error_fd);
+
+		FD_SET(gtd->attach_sock, &read_fd);
+		FD_SET(gtd->attach_sock, &error_fd);
+
+		rv = select(FD_SETSIZE, &read_fd, NULL, &error_fd, &timeout);
+
+		if (rv < 0)
+		{
+			gtd->attach_sock = close(gtd->attach_sock);
+	
+			show_message(gtd->ses, LIST_COMMAND, "#DAEMON UPDATE: UNATTACHING {%s} DUE TO SELECT ERROR.", gtd->attach_file);
+		}
+		else if (rv > 0)
+		{
+			if (FD_ISSET(gtd->attach_sock, &read_fd))
+			{
+				char buffer[BUFFER_SIZE];
+
+				rv = read(gtd->attach_sock, buffer, BUFFER_SIZE -1);
+
+				if (rv <= 0)
+				{
+					gtd->attach_sock = close(gtd->attach_sock);
+
+					winch_handler(0);
+
+					show_message(gtd->ses, LIST_COMMAND, "#DAEMON UPDATE: UNATTACHING {%s} DUE TO READ ERROR.", gtd->attach_file);
+				}
+				else
+				{
+					buffer[rv] = 0;
+/*
+					if (buffer[rv - 1] == (char) 255)
+					{
+						gtd->attach_sock = close(gtd->attach_sock);
+
+						show_message(gtd->ses, LIST_COMMAND, "\n#DAEMON {%s} SIGTSTP: UNATTACHING.", gtd->attach_file);
+
+						dirty_screen(gtd->ses);
+
+						return;
+					}
+*/
+					if (gtd->level->quiet == 0)
+					{
+						printf("%s", buffer);
+					}
+
+					if (FD_ISSET(gtd->attach_sock, &error_fd))
+					{
+						FD_CLR(gtd->attach_sock, &read_fd);
+
+						gtd->attach_sock = close(gtd->attach_sock);
+
+						show_message(gtd->ses, LIST_COMMAND, "#DAEMON UPDATE: UNATTACHING {%s} DUE TO EXCEPTION ERROR.", gtd->attach_file);
+					}
+				}
+			}
+			else
+			{
+				return;
+			}
+		}
+		fflush(stdout);
+	}
+
+}
+
+void update_chat(void)
 {
-	fd_set readfds, writefds, excfds;
-	static struct timeval to;
-	struct chat_data *buddy;
+	fd_set read_fd, write_fd, error_fd;
+	static struct timeval timeout;
+	struct chat_data *buddy, *buddy_next;
 	int rv;
 
-	open_timer(TIMER_POLL_CHAT);
+	open_timer(TIMER_UPDATE_CHAT);
 
 	if (gtd->chat)
 	{
-		FD_ZERO(&readfds);
-		FD_ZERO(&writefds);
-		FD_ZERO(&excfds);
+		for (buddy = gtd->chat->next ; buddy ; buddy = buddy_next)
+		{
+			buddy_next = buddy->next;
+
+			if (buddy->timeout && buddy->timeout < gtd->time)
+			{
+				chat_socket_printf(buddy, "<CHAT> Connection timed out.");
+
+				close_chat(buddy, TRUE);
+			}
+		}
+
+		if (gtd->chat->paste_time && gtd->chat->paste_time < gtd->utime)
+		{
+			chat_paste(NULL, NULL);
+		}
+
+		FD_ZERO(&read_fd);
+		FD_ZERO(&write_fd);
+		FD_ZERO(&error_fd);
 
-		FD_SET(gtd->chat->fd, &readfds);
+		FD_SET(gtd->chat->fd, &read_fd);
 
 		for (buddy = gtd->chat->next ; buddy ; buddy = buddy->next)
 		{
-			FD_SET(buddy->fd, &readfds);
-			FD_SET(buddy->fd, &writefds);
-			FD_SET(buddy->fd, &excfds);
+			FD_SET(buddy->fd, &read_fd);
+			FD_SET(buddy->fd, &write_fd);
+			FD_SET(buddy->fd, &error_fd);
 		}
 
-		rv = select(FD_SETSIZE, &readfds, &writefds, &excfds, &to);
+		rv = select(FD_SETSIZE, &read_fd, &write_fd, &error_fd, &timeout);
 
 		if (rv <= 0)
 		{
 			if (rv == 0 || errno == EINTR)
 			{
-				goto poll_chat_end;
+				goto update_chat_end;
 			}
-			syserr_fatal(-1, "poll_chat: select");
+			syserr_fatal(-1, "update_chat: select");
 		}
-		process_chat_connections(&readfds, &writefds, &excfds);
-	}
+		process_chat_connections(&read_fd, &write_fd, &error_fd);
 
-	poll_chat_end:
+	}
+	update_chat_end:
 
-	close_timer(TIMER_POLL_CHAT);
+	close_timer(TIMER_UPDATE_CHAT);
 }
 
-void poll_port(void)
+void update_port(void)
 {
 	struct session *ses;
-	fd_set readfds, writefds, excfds;
-	static struct timeval to;
+	fd_set read_fd, write_fd, error_fd;
+	static struct timeval timeout;
 	struct port_data *buddy;
 	int rv;
 
-	open_timer(TIMER_POLL_PORT);
+	open_timer(TIMER_UPDATE_PORT);
 
 	for (ses = gts->next ; ses ; ses = gtd->update)
 	{
@@ -336,20 +645,20 @@ void poll_port(void)
 
 		if (ses->port)
 		{
-			FD_ZERO(&readfds);
-			FD_ZERO(&writefds);
-			FD_ZERO(&excfds);
+			FD_ZERO(&read_fd);
+			FD_ZERO(&write_fd);
+			FD_ZERO(&error_fd);
 
-			FD_SET(ses->port->fd, &readfds);
+			FD_SET(ses->port->fd, &read_fd);
 
 			for (buddy = ses->port->next ; buddy ; buddy = buddy->next)
 			{
-				FD_SET(buddy->fd, &readfds);
-				FD_SET(buddy->fd, &writefds);
-				FD_SET(buddy->fd, &excfds);
+				FD_SET(buddy->fd, &read_fd);
+				FD_SET(buddy->fd, &write_fd);
+				FD_SET(buddy->fd, &error_fd);
 			}
 
-			rv = select(FD_SETSIZE, &readfds, &writefds, &excfds, &to);
+			rv = select(FD_SETSIZE, &read_fd, &write_fd, &error_fd, &timeout);
 
 			if (rv <= 0)
 			{
@@ -357,14 +666,14 @@ void poll_port(void)
 				{
 					continue;
 				}
-				syserr_fatal(-1, "poll_port: select");
+				syserr_fatal(-1, "update_port: select");
 			}
 
-			process_port_connections(ses, &readfds, &writefds, &excfds);
+			process_port_connections(ses, &read_fd, &write_fd, &error_fd);
 		}
 	}
 
-	close_timer(TIMER_POLL_PORT);
+	close_timer(TIMER_UPDATE_PORT);
 }
 
 void tick_update(void)
@@ -372,6 +681,7 @@ void tick_update(void)
 	struct session *ses;
 	struct listnode *node;
 	struct listroot *root;
+	char buf[BUFFER_SIZE];
 
 	open_timer(TIMER_UPDATE_TICKS);
 
@@ -391,20 +701,31 @@ void tick_update(void)
 			{
 				node->data = gtd->utime + (long long) (get_number(ses, node->arg3) * 1000000LL);
 
-				show_info(ses, LIST_DELAY, "#INFO TICK {%s} INITIALIZED WITH TIMESTAMP {%lld}", node->arg1, node->data);
+				show_info(ses, LIST_TICKER, "#INFO TICK {%s} INITIALIZED WITH TIMESTAMP {%lld}", node->arg1, node->data);
 			}
 
 			if (node->data <= gtd->utime)
 			{
 				node->data += (long long) (get_number(ses, node->arg3) * 1000000LL);
 
-				show_info(ses, LIST_DELAY, "#INFO TICK {%s} INITIALIZED WITH TIMESTAMP {%lld}", node->arg1, node->data);
+				show_info(ses, LIST_TICKER, "#INFO TICK {%s} INITIALIZED WITH TIMESTAMP {%lld}", node->arg1, node->data);
 
 				if (!HAS_BIT(root->flags, LIST_FLAG_IGNORE))
 				{
 					show_debug(ses, LIST_TICKER, "#DEBUG TICKER {%s}", node->arg2);
 
-					script_driver(ses, LIST_TICKER, node->arg2);
+					if (HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+					{
+						strcpy(buf, node->arg2);
+
+						delete_node_list(ses, LIST_TICKER, node);
+
+						script_driver(ses, LIST_TICKER, buf);
+					}
+					else
+					{
+						script_driver(ses, LIST_TICKER, node->arg2);
+					}
 				}
 			}
 		}
@@ -425,7 +746,7 @@ void delay_update(void)
 	{
 		gtd->update = ses->next;
 
-		root = ses->list[LIST_DELAY];	
+		root = ses->list[LIST_DELAY];
 
 		for (root->update = 0 ; root->update < root->used ; root->update++)
 		{
@@ -453,6 +774,40 @@ void delay_update(void)
 	close_timer(TIMER_UPDATE_DELAYS);
 }
 
+void path_update(void)
+{
+	struct session *ses;
+	struct listnode *node;
+	struct listroot *root;
+
+	open_timer(TIMER_UPDATE_PATHS);
+
+	for (ses = gts ; ses ; ses = gtd->update)
+	{
+		gtd->update = ses->next;
+
+		root = ses->list[LIST_PATH];
+
+		while (root->update < root->used)
+		{
+			node = root->list[root->update];
+
+			if (node->data > 0 && node->data <= gtd->utime)
+			{
+				root->update++;
+
+				node->data = 0;
+
+				show_debug(ses, LIST_PATH, "#DEBUG PATH {%s}", node->arg1);
+
+				script_driver(ses, LIST_PATH, node->arg1);
+			}
+			break;
+		}
+	}
+	close_timer(TIMER_UPDATE_PATHS);
+}
+
 void packet_update(void)
 {
 	char result[STRING_SIZE];
@@ -469,7 +824,8 @@ void packet_update(void)
 			if (HAS_BIT(ses->flags, SES_FLAG_SPLIT))
 			{
 				save_pos(ses);
-				goto_rowcol(ses, ses->bot_row, 1);
+
+				goto_pos(ses, ses->split->bot_row, 1);
 			}
 
 			SET_BIT(ses->flags, SES_FLAG_READMUD);
@@ -491,33 +847,6 @@ void packet_update(void)
 	close_timer(TIMER_UPDATE_PACKETS);
 }
 
-void chat_update(void)
-{
-	struct chat_data *buddy, *buddy_next;
-
-	open_timer(TIMER_UPDATE_CHAT);
-
-	if (gtd->chat)
-	{
-		for (buddy = gtd->chat->next ; buddy ; buddy = buddy_next)
-		{
-			buddy_next = buddy->next;
-
-			if (buddy->timeout && buddy->timeout < gtd->time)
-			{
-				chat_socket_printf(buddy, "<CHAT> Connection timed out.");
-
-				close_chat(buddy, TRUE);
-			}
-		}
-
-		if (gtd->chat->paste_time && gtd->chat->paste_time < gtd->utime)
-		{
-			chat_paste(NULL, NULL);
-		}
-	}
-	close_timer(TIMER_UPDATE_CHAT);
-}
 
 
 void terminal_update(void)
@@ -535,9 +864,10 @@ void terminal_update(void)
 			show_vtmap(ses);
 
 			check_all_events(ses, SUB_ARG|SUB_SEC, 0, 0, "MAP UPDATED VTMAP");
+
+			SET_BIT(gtd->flags, TINTIN_FLAG_FLUSH);
 		}
 	}
-	fflush(stdout);
 
 	close_timer(TIMER_UPDATE_TERMINAL);
 }
@@ -735,8 +1065,9 @@ long long display_timer(struct session *ses, int timer)
 		timer_table[timer].name,
 		gtd->timer[timer][0] / gtd->timer[timer][1],
 		gtd->timer[timer][3] / gtd->timer[timer][4] / 1000,
-		100.0 * (double) indicated_usage / (double) gtd->total_io_exec,
-		100.0 * (double) indicated_usage / (double) total_usage);
+		(double) (100000 * indicated_usage / gtd->total_io_exec) / 1000.0,
+		(double) (100000 * indicated_usage / total_usage) / 1000.0
+		);
 
 	return indicated_usage;
 }

+ 9 - 8
src/utf8.c

@@ -32,10 +32,10 @@ struct interval_type
 	int tail;
 };
 
-// Copyright 2014-2019 Igor van den Hoven
-
 static int boundless_binary_search(struct interval_type *table, int size, int key)
 {
+	// Copyright 2014-2019 Igor van den Hoven
+
 	int mid, i;
 
 	mid = i = size - 1;
@@ -67,13 +67,13 @@ int linear_search(struct interval_type *table, int size, int key)
 	{
 		if (table[i].head > table[i].tail)
 		{
-			printf("table[%d].head < table[%d].tail\n", i, i);
+			print_stdout("table[%d].head < table[%d].tail\n", i, i);
 		}
 		if (i < size - 1)
 		{
 			if (table[i].head >= table[i+1].head)
 			{
-				printf("table[%d].head >= table[%d].head\n", i, i+1);
+				print_stdout("table[%d].head >= table[%d].head\n", i, i+1);
 			}
 		}
 	}
@@ -222,7 +222,7 @@ int get_utf8_size(char *str)
 		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 		2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-		3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,1,1,1,1,1,1,1,1
+		3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
 	};
 	unsigned char *ptu = (unsigned char *) str;
 
@@ -253,9 +253,8 @@ int get_utf8_size(char *str)
 				return 1;
 			}
 			return 4;
-
 		default:
-			return -1;
+			return 1;
 	}
 }
 
@@ -270,7 +269,7 @@ int get_utf8_width(char *str, int *width)
 		1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 		1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 		2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-		3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,1,1,1,1,1,1,1,1
+		3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
 	};
 	int size, index;
 	unsigned char *ptu = (unsigned char *) str;
@@ -305,6 +304,8 @@ int get_utf8_width(char *str, int *width)
 			}
 			index = (int) ptu[3] - 128 + 64 * (ptu[2] - 128) + 4096 * (ptu[1] - 128) + 262144 * (ptu[0] - 240);
 			break;
+		default:
+			return 1;
 	}
 
 	*width = unicode_width(index);

+ 28 - 34
src/utils.c

@@ -26,37 +26,6 @@
 
 #include "tintin.h"
 
-int is_color_code(char *str)
-{
-	if (str[0] == '<')
-	{
-		if (!isalnum((int) str[1]) || !isalnum((int) str[2]) || !isalnum((int) str[3]) || str[4] != '>')
-		{
-			return FALSE;
-		}
-		else if (str[1] >= '0' && str[1] <= '9' && str[2] >= '0' && str[2] <= '9' && str[3] >= '0' && str[3] <= '9')
-		{
-			return TRUE;
-		}
-		else if (str[1] >= 'a' && str[1] <= 'f' && str[2] >= 'a' && str[2] <= 'f' && str[3] >= 'a' && str[3] <= 'f')
-		{
-			return TRUE;
-		}
-		else if (str[1] >= 'A' && str[1] <= 'F' && str[2] >= 'A' && str[2] <= 'F' && str[3] >= 'A' && str[3] <= 'F')
-		{
-			return TRUE;
-		}
-		else if (str[1] == 'g' && str[2] >= '0' && str[2] <= '9' && str[3] >= '0' && str[3] <= '9')
-		{
-			return TRUE;
-		}
-		else if (str[1] == 'G' && str[2] >= '0' && str[2] <= '9' && str[3] >= '0' && str[3] <= '9')
-		{
-			return TRUE;
-		}
-	}
-	return FALSE;
-}
 
 int hex_digit(char *str)
 {
@@ -357,16 +326,27 @@ unsigned long long utime()
 
 void seed_rand(struct session *ses, unsigned long long seed)
 {
-	ses->rand = seed;
+	ses->rand = seed % 4294967291ULL;
 }
 
 unsigned long long generate_rand(struct session *ses)
 {
-	ses->rand = 6364136223846793005ULL * ses->rand + 1ULL;
+	ses->rand = ses->rand * 279470273ULL % 4294967291ULL;
+
+//	return ses->rand % 1000000000ULL;
 
 	return ses->rand;
 }
+/*
+uint32_t lcg_rand(uint32_t *state)
+{
+    return *state = (uint64_t)*state * 279470273u % 0xfffffffb;
+}
+	ses->rand = 6364136223846793005ULL * ses->rand + 1ULL;
 
+	return ses->rand;
+}
+*/
 char *capitalize(char *str)
 {
 	static char outbuf[BUFFER_SIZE];
@@ -393,6 +373,20 @@ char *ntos(long long number)
 	return outbuf[cnt];
 }
 
+char *indent_one(int len)
+{
+	static char outbuf[10][STACK_SIZE];
+	static int cnt;
+
+	cnt = (cnt + 1) % 10;
+
+	memset(outbuf[cnt], ' ', UMAX(1, len));
+
+	outbuf[cnt][len] = 0;
+
+	return outbuf[cnt];
+}
+
 char *indent(int len)
 {
 	static char outbuf[10][STACK_SIZE];
@@ -400,7 +394,7 @@ char *indent(int len)
 
 	cnt = (cnt + 1) % 10;
 
-	memset(outbuf[cnt], ' ', len * 5);
+	memset(outbuf[cnt], ' ', UMAX(1, len * 5));
 
 	outbuf[cnt][len * 5] = 0;
 

+ 148 - 123
src/variable.c

@@ -69,6 +69,11 @@ DO_COMMAND(do_variable)
 	}
 	else
 	{
+		if (is_math(ses, arg1))
+		{
+			show_message(ses, LIST_VARIABLE, "#VARIABLE: INVALID VARIALBE NAME {%s}.", arg1);
+			return ses;
+		}
 		str = str_alloc(UMAX(strlen(arg), BUFFER_SIZE));
 
 		arg = sub_arg_in_braces(ses, arg, str, GET_ALL, SUB_VAR|SUB_FUN);
@@ -203,7 +208,7 @@ DO_COMMAND(do_replace)
 		}
 	}
 
-	if (tintin_regexp(ses, NULL, node->arg2, arg2, 0, SUB_CMD) == FALSE)
+	if (tintin_regexp(ses, NULL, node->arg2, arg2, 0, REGEX_FLAG_CMD) == FALSE)
 	{
 		show_message(ses, LIST_VARIABLE, "#REPLACE: {%s} NOT FOUND IN {%s}.", arg2, node->arg2);
 	}
@@ -234,7 +239,7 @@ DO_COMMAND(do_replace)
 
 			pti = ptm + strlen(gtd->cmds[0]);
 		}
-		while (tintin_regexp(ses, NULL, pti, arg2, 0, SUB_CMD));
+		while (tintin_regexp(ses, NULL, pti, arg2, 0, REGEX_FLAG_CMD));
 
 		strcat(buf, pti);
 
@@ -247,17 +252,31 @@ DO_COMMAND(do_replace)
 	support routines for #format
 */
 
+unsigned long long generate_hash_key(char *str)
+{
+	unsigned long long len, h = 4321;
+
+	for (len = 0 ; *str != 0 ; str++, len++)
+	{
+		h = ((h << 5) + h) + *str;
+	}
+
+	h += len;
+
+	return h;
+}
+
 void numbertocharacter(struct session *ses, char *str)
 {
 	if (get_number(ses, str) < 256)
 	{
 		sprintf(str, "%c", (int) get_number(ses, str));
 	}
-	else if (HAS_BIT(ses->flags, SES_FLAG_BIG5))
+	else if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5))
 	{
 		sprintf(str, "%c%c", (unsigned int) get_number(ses, str) % 256, (unsigned int) get_number(ses, str) / 256);
 	}
-	else if (HAS_BIT(ses->flags, SES_FLAG_UTF8))
+	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
 	{
 		unicode_to_utf8((int) get_number(ses, str), str);
 	}
@@ -271,11 +290,11 @@ void charactertonumber(struct session *ses, char *str)
 {
 	int result;
 
-	if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && is_big5(str))
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && is_big5(str))
 	{
 		result = (unsigned char) str[0] + (unsigned char) str[1] * 256;
 	}
-	else if (HAS_BIT(ses->flags, SES_FLAG_UTF8) && is_utf8_head(str))
+	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(str))
 	{
 		get_utf8_index(str, &result);
 	}
@@ -286,6 +305,27 @@ void charactertonumber(struct session *ses, char *str)
 	sprintf(str, "%d", result);
 }
 
+void charactertohex(struct session *ses, char *str)
+{
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && is_big5(str))
+	{
+		sprintf(str, "%u", (unsigned char) str[0] + (unsigned char) str[1] * 256);
+	}
+	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(str))
+	{
+		int result;
+
+		get_utf8_index(str, &result);
+
+		sprintf(str, "%u", result);
+	}
+	else if (!is_math(ses, str))
+	{
+		sprintf(str, "%u", (unsigned int) str[0]);
+	}
+}
+
+
 void colorstring(struct session *ses, char *str)
 {
 	char result[BUFFER_SIZE];
@@ -321,7 +361,7 @@ int get_color_names(struct session *ses, char *string, char *result)
 		{
 			for (cnt = 0 ; *color_table[cnt].name ; cnt++)
 			{
-				if (is_abbrev(color_table[cnt].name, string))
+				if (!strncmp(color_table[cnt].name, string, color_table[cnt].len))
 				{
 					substitute(ses, color_table[cnt].code, result, SUB_COL);
 
@@ -333,9 +373,23 @@ int get_color_names(struct session *ses, char *string, char *result)
 
 			if (*color_table[cnt].name == 0)
 			{
-				return FALSE;
-			}
+				for (cnt = 0 ; *color_table[cnt].name ; cnt++)
+				{
+					if (!strncasecmp(color_table[cnt].name, string, color_table[cnt].len))
+					{
+						substitute(ses, color_table[cnt].code, result, SUB_COL);
+
+						result += strlen(result);
+
+						break;
+					}
+				}
 
+				if (*color_table[cnt].name == 0)
+				{
+					return FALSE;
+				}
+			}
 			string += strlen(color_table[cnt].name);
 		}
 
@@ -492,126 +546,71 @@ void stripspaces(char *str)
 //	strcpy(str, &str[cnt]);
 }
 
-void wrapstring(struct session *ses, char *str)
+void wrapstring(struct session *ses, char *str, char *wrap)
 {
-	char *pti, *lis, *sos, *soc, buf[BUFFER_SIZE], color[BUFFER_SIZE], tmp[BUFFER_SIZE], sec[BUFFER_SIZE], arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], *arg;
-	int col = 1, cnt = 1, len, size, width;
+	char  arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
+	char *pts, *pte, *arg;
+	int cnt, width, height;
 
-	push_call("wrapstring(%p,%p)",ses,str);
+	push_call("wrapstring(%p,%p,%p)",ses,str,wrap);
 
-	arg = get_arg_in_braces(ses, str, buf, GET_ALL);
-
-	substitute(ses, buf, arg1, SUB_COL|SUB_ESC);
+	arg = sub_arg_in_braces(ses, str, arg1, GET_ALL, SUB_COL);
 
 	if (*arg == COMMAND_SEPARATOR)
 	{
 		arg++;
 	}
+
 	arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
 
 	if (*arg2)
 	{
-		len = get_number(ses, arg2);
-
-		if (len <= 0)
-		{
-			show_error(ses, LIST_VARIABLE, "#FORMAT %w: INVALID LENTGH {%s}", arg2);
-			len = gtd->screen->cols;
-		}
+		cnt = get_number(ses, arg2);
+	}
+	else if (*wrap)
+	{
+		cnt = atoi(wrap);
 	}
 	else
 	{
-		len = gtd->screen->cols;
+		cnt = get_scroll_cols(ses);
 	}
 
-	pti = lis = sos = soc = arg1;
-
-	buf[0] = color[0] = 0;
-
-	while (*pti != 0)
+	if (cnt <= 0)
 	{
-		if (skip_vt102_codes(pti))
-		{
-			pti += skip_vt102_codes(pti);
-
-			continue;
-		}
-
-		if (*pti == ' ' || *pti == '\n')
-		{
-			lis = pti;
-		}
+		cnt = get_scroll_cols(ses) + cnt;
 
-		if (col > len || *pti == '\n')
+		if (cnt <= 0)
 		{
-			col = 1;
-
-			if (pti - lis >= len || pti - lis > 15)
-			{
-				sprintf(tmp, "%.*s", (int) (sos - soc), soc);
-
-				get_color_codes(color, tmp, color);
-
-				sprintf(tmp, "%.*s", (int) (pti - sos), sos);
-
-				substitute(ses, tmp, sec, SUB_SEC);
-
-				cat_sprintf(buf, "{%d}{%s%s}", cnt++, color, sec);
+			show_error(ses, LIST_VARIABLE, "#FORMAT %w: INVALID LENTGH {%s}", arg2);
 
-				soc = sos;
-				lis = sos = pti;
-			}
-			else
-			{
-				sprintf(tmp, "%.*s", (int) (sos - soc), soc);
+			pop_call();
+			return;
+		}
+	}
 
-				get_color_codes(color, tmp, color);
+	word_wrap_split(ses, arg1, arg2, cnt, 0, 0, 0, &height, &width);
 
-				sprintf(tmp, "%.*s", (int) (lis - sos), sos);
+	pts = pte = arg2;
 
-				substitute(ses, tmp, sec, SUB_SEC);
+	str[0] = cnt = 0;
 
-				cat_sprintf(buf, "{%d}{%s%s}", cnt++, color, sec);
+	while (*pte != 0)
+	{
+		if (*pte == '\n')
+		{
+			*pte++ = 0;
 
-				lis++;
+			cat_sprintf(str, "{%d}{%s}", ++cnt, pts);
 
-				soc = sos;
-				pti = sos = lis;
-			}
+			pts = pte;
 		}
 		else
 		{
-			if (HAS_BIT(ses->flags, SES_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
-			{
-				pti++;
-				pti++;
-				col++;
-			}
-			else if (HAS_BIT(ses->flags, SES_FLAG_UTF8) && is_utf8_head(pti))
-			{
-				size = get_utf8_width(pti, &width);
-
-				pti += size;
-				col += width;
-			}
-			else
-			{
-				pti++;
-				col++;
-			}
+			pte++;
 		}
 	}
-	sprintf(tmp, "%.*s", (int) (sos - soc), soc);
-
-	get_color_codes(color, tmp, color);
-
-	sprintf(tmp, "%s", sos);
-
-	substitute(ses, tmp, sec, SUB_SEC);
-
-	cat_sprintf(buf, "{%d}{%s%s}", cnt, color, sec);
-
-	strcpy(str, buf);
+	cat_sprintf(str, "{%d}{%s}", ++cnt, pts);
 
 	pop_call();
 	return;
@@ -631,7 +630,7 @@ int stringlength(struct session *ses, char *str)
 
 int string_str_raw_len(struct session *ses, char *str, int start, int end)
 {
-	int raw_cnt, str_cnt, ret_cnt, tmp_cnt, tot_len, width;
+	int raw_cnt, str_cnt, ret_cnt, tmp_cnt, tot_len, width, col_len;
 
 	raw_cnt = str_cnt = ret_cnt = 0;
 
@@ -647,10 +646,12 @@ int string_str_raw_len(struct session *ses, char *str, int start, int end)
 			continue;
 		}
 
-		if (is_color_code(&str[raw_cnt]))
+		col_len = is_color_code(&str[raw_cnt]);
+		
+		if (col_len)
 		{
-			ret_cnt += (str_cnt >= start) ? 5 : 0;
-			raw_cnt += 5;
+			ret_cnt += (str_cnt >= start) ? col_len : 0;
+			raw_cnt += col_len;
 
 			continue;
 		}
@@ -674,7 +675,7 @@ int string_str_raw_len(struct session *ses, char *str, int start, int end)
 			continue;
 		}
 
-		if (HAS_BIT(ses->flags, SES_FLAG_UTF8) && is_utf8_head(&str[raw_cnt]))
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&str[raw_cnt]))
 		{
 			tmp_cnt = get_utf8_width(&str[raw_cnt], &width);
 
@@ -697,17 +698,17 @@ int string_str_raw_len(struct session *ses, char *str, int start, int end)
 
 // raw range stripped return
 
-int string_raw_str_len(struct session *ses, char *str, int start, int end)
+int string_raw_str_len(struct session *ses, char *str, int raw_start, int raw_end)
 {
-	int raw_cnt, ret_cnt, tot_len, width;
+	int raw_cnt, ret_cnt, tot_len, width, col_len;
 
-	raw_cnt = start;
+	raw_cnt = raw_start;
 	ret_cnt = 0;
 	tot_len = strlen(str);
 
 	while (raw_cnt < tot_len)
 	{
-		if (raw_cnt >= end)
+		if (raw_cnt >= raw_end)
 		{
 			break;
 		}
@@ -719,9 +720,11 @@ int string_raw_str_len(struct session *ses, char *str, int start, int end)
 			continue;
 		}
 
-		if (is_color_code(&str[raw_cnt]))
+		col_len = is_color_code(&str[raw_cnt]);
+		
+		if (col_len)
 		{
-			raw_cnt += 5;
+			raw_cnt += col_len;
 
 			continue;
 		}
@@ -738,7 +741,7 @@ int string_raw_str_len(struct session *ses, char *str, int start, int end)
 			continue;
 		}
 
-		if (HAS_BIT(gtd->ses->flags, SES_FLAG_UTF8) && is_utf8_head(&str[raw_cnt]))
+		if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&str[raw_cnt]))
 		{
 			raw_cnt += get_utf8_width(&str[raw_cnt], &width);
 
@@ -782,10 +785,25 @@ void timestring(struct session *ses, char *str)
 	strftime(str, BUFFER_SIZE, arg1, &timeval_tm);
 }
 
+void justify_string(struct session *ses, char *in, char *out, int align, int cut)
+{
+	char temp[BUFFER_SIZE];
+
+	if (align < 0)
+	{
+		sprintf(temp, "%%%d.%ds", align - ((int) strlen(in) - string_raw_str_len(ses, in, 0, BUFFER_SIZE)), string_str_raw_len(ses, in, 0, cut));
+	}
+	else
+	{
+		sprintf(temp, "%%%d.%ds", align + ((int) strlen(in) - string_raw_str_len(ses, in, 0, BUFFER_SIZE)), string_str_raw_len(ses, in, 0, cut));
+	}
+
+	sprintf(out, temp, in);
+}
 
 void format_string(struct session *ses, char *format, char *arg, char *out)
 {
-	char temp[BUFFER_SIZE], newformat[BUFFER_SIZE], arglist[30][20000], *ptf, *ptt, *pts, *ptn;
+	char argformat[BUFFER_SIZE], newformat[BUFFER_SIZE], arglist[30][20000], *ptf, *ptt, *pts, *ptn;
 	struct tm timeval_tm;
 	time_t    timeval_t;
 	int i;
@@ -847,11 +865,17 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 
 				if (*ptf == 'd' || *ptf == 'f' || *ptf == 'X')
 				{
-					strcpy(temp, pts);
+					strcpy(argformat, pts);
 
 					ptn = pts + 1;
 					*ptn = 0;
 				}
+				else if (*ptf == 'w')
+				{
+					strcpy(argformat, pts+1);
+					ptn = pts + 1;
+					*ptn = 0;
+				}
 				else if (pts[1])
 				{
 					char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
@@ -870,11 +894,11 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 					{
 						if (atoi(arg1) < 0)
 						{
-							sprintf(temp, "%%%d", atoi(arg1) - ((int) strlen(arglist[i]) - string_raw_str_len(ses, arglist[i], 0, BUFFER_SIZE)));
+							sprintf(argformat, "%%%d", atoi(arg1) - ((int) strlen(arglist[i]) - string_raw_str_len(ses, arglist[i], 0, BUFFER_SIZE)));
 						}
 						else
 						{
-							sprintf(temp, "%%%d", atoi(arg1) + ((int) strlen(arglist[i]) - string_raw_str_len(ses, arglist[i], 0, BUFFER_SIZE)));
+							sprintf(argformat, "%%%d", atoi(arg1) + ((int) strlen(arglist[i]) - string_raw_str_len(ses, arglist[i], 0, BUFFER_SIZE)));
 						}
 					}
 					else
@@ -891,19 +915,19 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 
 						if (atoi(arg1) < 0)
 						{
-							sprintf(temp, "%%%d.%d",
+							sprintf(argformat, "%%%d.%d",
 								atoi(arg1) - ((int) strlen(arglist[i]) - string_raw_str_len(ses, arglist[i], 0, BUFFER_SIZE)),
 								string_str_raw_len(ses, arglist[i], 0, atoi(arg2)));
 						}
 						else
 						{
-							sprintf(temp, "%%%d.%d",
+							sprintf(argformat, "%%%d.%d",
 								atoi(arg1) + ((int) strlen(arglist[i]) - string_raw_str_len(ses, arglist[i], 0, BUFFER_SIZE)),
 								string_str_raw_len(ses, arglist[i], 0, atoi(arg2)));
 						}
 					}
 
-					ptt = temp;
+					ptt = argformat;
 					ptn = pts;
 
 					while (*ptt)
@@ -926,13 +950,13 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 						break;
 
 					case 'd':
-						strcat(temp, "lld");
-						sprintf(arglist[i], temp, (long long) get_number(ses, arglist[i]));
+						strcat(argformat, "lld");
+						sprintf(arglist[i], argformat, (long long) get_number(ses, arglist[i]));
 						break;
 
 					case 'f':
-						strcat(temp, "Lf");
-						sprintf(arglist[i], temp, get_double(ses, arglist[i]));
+						strcat(argformat, "Lf");
+						sprintf(arglist[i], argformat, get_double(ses, arglist[i]));
 						break;
 
 					case 'g':
@@ -976,7 +1000,7 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 
 					case 'w':
 						substitute(ses, arglist[i], arglist[i], SUB_VAR|SUB_FUN);
-						wrapstring(ses, arglist[i]);
+						wrapstring(ses, arglist[i], argformat);
 						break;
 
 					case 'x':
@@ -1038,8 +1062,9 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 						break;
 
 					case 'X':
-						strcat(temp, "llX");
-						sprintf(arglist[i], temp, (unsigned long long) get_number(ses, arglist[i]));
+						strcat(argformat, "llX");
+						charactertohex(ses, arglist[i]);
+						sprintf(arglist[i], argformat, (unsigned long long) get_number(ses, arglist[i]));
 						break;
 
 					// undocumented

+ 181 - 46
src/vt102.c

@@ -26,61 +26,117 @@
 
 #include "tintin.h"
 
-void save_pos(struct session *ses)
+void init_pos(struct session *ses, int row, int col)
 {
-	if (ses->cur_row == gtd->screen->rows)
-	{
-		printf("\e7"); 
+	push_call("init_pos(%p)",ses);
 
-		ses->sav_row = ses->cur_row;
-		ses->sav_col = ses->cur_col;
-	}
+	goto_pos(ses, row, col);
+
+	gtd->screen->sav_row[0] = ses->cur_row;
+	gtd->screen->sav_col[0] = ses->cur_col;
+
+	gtd->screen->sav_lev = 1;
+
+	pop_call();
+	return;
 }
 
-void load_pos(struct session *ses)
+void save_pos(struct session *ses)
 {
-	printf("\e8\e8"); 
+	push_call("save_pos(%p)",ses);
+
+	gtd->screen->sav_row[gtd->screen->sav_lev] = ses->cur_row;
+	gtd->screen->sav_col[gtd->screen->sav_lev] = ses->cur_col;
+
+	if (gtd->screen->sav_lev < STACK_SIZE)
+	{
+		gtd->screen->sav_lev++;
+	}
+	else
+	{
+		syserr_printf(ses, "sav_lev++ above 1000.");
+	}
+	print_stdout("\e[?25l");
 
-	ses->cur_row = ses->sav_row;
-	ses->cur_col = ses->sav_col;
+	pop_call();
+	return;
 }
 
 void restore_pos(struct session *ses)
 {
-	printf("\e8\e8"); 
+	if (gtd->screen->sav_lev > 0)
+	{
+		gtd->screen->sav_lev--;
+	}
+	else
+	{
+		gtd->screen->sav_lev = 0;
+		syserr_printf(ses, "sav_lev-- below 0.");
+	}
+
+	if (gtd->screen->sav_row[gtd->screen->sav_lev] == gtd->screen->rows)
+	{
+		goto_pos(ses, gtd->screen->rows, inputline_cur_pos());
+	}
+	else
+	{
+		goto_pos(ses, gtd->screen->sav_row[gtd->screen->sav_lev], gtd->screen->sav_col[gtd->screen->sav_lev]);
+	}
+	print_stdout("\e[?25h");
 
-	ses->cur_row = ses->sav_row;
-	ses->cur_col = ses->sav_col;
+	SET_BIT(gtd->flags, TINTIN_FLAG_FLUSH);
 }
 
 void goto_pos(struct session *ses, int row, int col)
 {
-	printf("\e[%d;%dH", row, col);
+	print_stdout("\e[%d;%dH", row, col);
 
+	if (col < 1)
+	{
+		print_stdout("debug: goto_pos(%d,%d)\n",row,col);
+	}
 	ses->cur_row = row;
 	ses->cur_col = col;
 }
 
-void goto_rowcol(struct session *ses, int row, int col)
+void erase_cols(int cnt)
 {
-	printf("\e[%d;%dH", row, col);
+	if (cnt)
+	{
+		print_stdout("\e[%dX", cnt);
+	}
+}
 
-	ses->cur_row = row;
-	ses->cur_col = col;
+void erase_row(struct session *ses)
+{
+	if (ses->wrap == gtd->screen->cols || ses->cur_row == gtd->screen->rows)
+	{
+		print_stdout("\e[2K");
+	}
+	else
+	{
+		save_pos(ses);
+
+		goto_pos(ses, ses->cur_row, ses->split->top_col);
+
+		erase_cols(ses->wrap);
+
+		restore_pos(ses);
+	}
 }
 
 void erase_lines(struct session *ses, int rows)
 {
-	printf("\e[%dM", rows);
+	print_stdout("\e[%dM", rows);
 }
 
 void erase_toeol(void)
 {
-	printf("\e[K");
+	print_stdout("\e[K");
 }
 
 /*
-	doesn't do much
+	unused
 */
 
 void reset(struct session *ses)
@@ -88,7 +144,7 @@ void reset(struct session *ses)
 	ses->cur_row = 1;
 	ses->cur_col = 1;
 
-	printf("\ec");
+	print_stdout("\ec");
 }
 
 
@@ -96,26 +152,27 @@ void scroll_region(struct session *ses, int top, int bot)
 {
 	push_call("scroll_region(%p,%d,%d)",ses,top,bot);
 
-	printf("\e[%d;%dr", top, bot);
+	print_stdout("\e[%d;%dr", top, bot);
 
-	ses->top_row = top;
-	ses->bot_row = bot;
+	ses->split->top_row = top;
+	ses->split->bot_row = bot;
 
-	check_all_events(ses, SUB_ARG, 0, 4, "VT100 SCROLL REGION", ntos(top), ntos(bot), ntos(gtd->screen->rows), ntos(gtd->screen->cols), ntos(ses->wrap > 0 ? ses->wrap : gtd->screen->cols));
+	check_all_events(ses, SUB_ARG, 0, 4, "VT100 SCROLL REGION", ntos(top), ntos(bot), ntos(gtd->screen->rows), ntos(gtd->screen->cols), ntos(get_scroll_cols(ses)));
 
 	pop_call();
 	return;
 }
 
-
 void reset_scroll_region(struct session *ses)
 {
 	if (ses == gtd->ses)
 	{
-		printf("\e[r");
+		print_stdout("\e[r");
 	}
-	ses->top_row = 1;
-	ses->bot_row = gtd->screen->rows;
+	ses->split->top_row = 1;
+	ses->split->top_col = 1;
+	ses->split->bot_row = gtd->screen->rows;
+	ses->split->bot_col = gtd->screen->cols;
 }
 
 
@@ -169,6 +226,10 @@ int skip_vt102_codes(char *str)
 		case ']':
 			switch (str[2])
 			{
+				case 0:
+					pop_call();
+					return 2;
+
 				case 'P':
 					for (skip = 3 ; skip < 10 ; skip++)
 					{
@@ -182,7 +243,7 @@ int skip_vt102_codes(char *str)
 
 				case 'R':
 					pop_call();
-					return 3;
+					return str[3] ? 3 : 2;
 			}
 			pop_call();
 			return 2;
@@ -216,7 +277,7 @@ int skip_vt102_codes(char *str)
 	return skip;
 }
 
-int find_non_color_codes(char *str)
+int find_color_code(char *str)
 {
 	int skip;
 
@@ -356,8 +417,6 @@ int skip_vt102_codes_non_graph(char *str)
 	return 0;
 }
 
-
-
 void strip_vt102_codes(char *str, char *buf)
 {
 	char *pti, *pto;
@@ -471,19 +530,21 @@ char *strip_vt102_strstr(char *str, char *buf, int *len)
 
 // mix old and str, then copy compressed color string to buf which can point to old.
 
-void get_color_codes(char *old, char *str, char *buf)
+void get_color_codes(char *old, char *str, char *buf, int flags)
 {
 	char *pti, *pto, col[100], tmp[BUFFER_SIZE];
 	int len, vtc, fgc, bgc, cnt;
 	int rgb[6] = { 0, 0, 0, 0, 0, 0 };
 
+	push_call("get_color_codes(%p,%p,%p,%d)",old,str,buf,flags);
+
 	pto = tmp;
 
 	pti = old;
 
 	while (*pti)
 	{
-		while ((len = find_non_color_codes(pti)) != 0)
+		while ((len = find_color_code(pti)) != 0)
 		{
 			memcpy(pto, pti, len);
 			pti += len;
@@ -500,13 +561,23 @@ void get_color_codes(char *old, char *str, char *buf)
 
 	while (*pti)
 	{
-		while ((len = find_non_color_codes(pti)) != 0)
+		while ((len = find_color_code(pti)) != 0)
 		{
 			memcpy(pto, pti, len);
 			pti += len;
 			pto += len;
 		}
 
+		if (!HAS_BIT(flags, GET_ALL))
+		{
+			break;
+		}
+
+		if (*pti == '\n')
+		{
+			break;
+		}
+
 		if (*pti)
 		{
 			pti++;
@@ -518,6 +589,7 @@ void get_color_codes(char *old, char *str, char *buf)
 	if (strlen(tmp) == 0)
 	{
 		buf[0] = 0;
+		pop_call();
 		return;
 	}
 
@@ -753,6 +825,9 @@ void get_color_codes(char *old, char *str, char *buf)
 	}
 
 	strcat(buf, "m");
+
+	pop_call();
+	return;
 }
 
 int strip_vt102_strlen(struct session *ses, char *str)
@@ -771,7 +846,12 @@ int strip_vt102_strlen(struct session *ses, char *str)
 			continue;
 		}
 
-		if (HAS_BIT(ses->flags, SES_FLAG_UTF8) && is_utf8_head(pti))
+		if (*pti == '\t')
+		{
+			i += ses->tab_width - i % ses->tab_width;
+			pti++;
+		}
+		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
 		{
 			pti += get_utf8_width(pti, &w);
 
@@ -786,6 +866,61 @@ int strip_vt102_strlen(struct session *ses, char *str)
 	return i;
 }
 
+
+int strip_vt102_width(struct session *ses, char *str, int *lines)
+{
+	char *pti;
+	int w, i = 0, max = 0;
+
+	*lines = 1;
+
+	pti = str;
+
+	while (*pti)
+	{
+		if (*pti == '\n')
+		{
+			*lines += 1;
+
+			if (i > max)
+			{
+				max = i;
+				i = 0;
+			}
+		}
+
+		if (skip_vt102_codes(pti))
+		{
+			pti += skip_vt102_codes(pti);
+
+			continue;
+		}
+
+		if (*pti == '\t')
+		{
+			i += ses->tab_width - i % ses->tab_width;
+			pti++;
+		}
+		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
+		{
+			pti += get_utf8_width(pti, &w);
+
+			i += w;
+		}
+		else
+		{
+			pti++;
+			i++;
+		}
+	}
+
+	if (i > max)
+	{
+		return i;
+	}
+	return max;
+}
+
 int strip_color_strlen(struct session *ses, char *str)
 {
 	char buf[BUFFER_SIZE];
@@ -929,16 +1064,16 @@ int interpret_vt102_codes(struct session *ses, char *str, int real)
 				break;
 
 			case 'r':
-				if (sscanf(data, "%d;%d", &ses->top_row, &ses->bot_row) != 2)
+				if (sscanf(data, "%d;%d", &ses->split->top_row, &ses->split->bot_row) != 2)
 				{
-					if (sscanf(data, "%d", &ses->top_row) != 1)
+					if (sscanf(data, "%d", &ses->split->top_row) != 1)
 					{
-						ses->top_row = 1;
-						ses->bot_row = gtd->screen->rows;
+						ses->split->top_row = 1;
+						ses->split->bot_row = gtd->screen->rows;
 					}
 					else
 					{
-						ses->bot_row = gtd->screen->rows;
+						ses->split->bot_row = gtd->screen->rows;
 					}
 				}
 				ses->cur_row = 1;
@@ -971,9 +1106,9 @@ int interpret_vt102_codes(struct session *ses, char *str, int real)
 			ses->cur_col = URANGE(1, ses->cur_col, gtd->screen->cols + 1);
 
 
-			ses->top_row = URANGE(1, ses->top_row, gtd->screen->rows);
+			ses->split->top_row = URANGE(1, ses->split->top_row, gtd->screen->rows);
 
-			ses->bot_row = ses->bot_row ? URANGE(1, ses->bot_row, gtd->screen->rows) : gtd->screen->rows;
+			ses->split->bot_row = ses->split->bot_row ? URANGE(1, ses->split->bot_row, gtd->screen->rows) : gtd->screen->rows;
 			
 			return TRUE;
 		}

Деякі файли не було показано, через те що забагато файлів було змінено