utalk-1.0.1.beta.orig/0040755000076600007650000000000006771515503014020 5ustar garabikatlasutalk-1.0.1.beta.orig/README0100644000076600007650000000201606376753221014676 0ustar garabikatlasutalk - a UDP-based full-screen talk protocol and client -------------------------------------------------------- This is a beta version of utalk, and the documentation is not yet complete. Please see the file PROTOCOL for the utalk protocol specifications, and the manpage (utalk.1) for general documentation. To compile utalk, edit the Makefile to suit your system, and run ``make''. utalk requires an ANSI C compiler such as gcc, and should be fairly portable on most modern Unix-like systems. utalk has been tested on a 64-bit system (Linux/axp), and should work without modification on any system where ints are 32 bit, shorts 16 bit and chars 8 bit. If this is not the case for your system, edit the definitions for srdp_u32, srdp_u16 and srdp_u8 in srdpdata.h. If you have trouble compiling or getting utalk to run on some platform, please tell me about it. Once compiled, the utalk binary is ready to run; you can install it on the system by typing ``make install''. -- Roger Espel Llima utalk-1.0.1.beta.orig/Makefile0100644000076600007650000000453106376762674015476 0ustar garabikatlas# Makefile for srdp/utalk - Roger Espel Llima #BINDIR = /usr/local/bin #MANDIR = /usr/local/man #LIBDIR = /usr/local/lib #BINDIR = /usr/local/util/bin #MANDIR = /usr/local/util/man #LIBDIR = /usr/local/util/lib BINDIR = /home/e/espel/bin MANDIR = /home/e/espel/man LIBDIR = /home/e/espel/lib SRCS = srdp.c utalk.c util.c termcap.c termio.c globals.c signal.c screen.c\ functions.c kbd.c comm.c rc.c menu.c OBJS = srdp.o utalk.o util.o termcap.o termio.o globals.o signal.o screen.o\ functions.o kbd.o comm.o rc.o menu.o HEADS = comm.h kbd.h signal.h struct.h util.h functions.h rc.h srdp.h\ termcap.h globals.h screen.h srdpdata.h termio.h menu.h # The termcap library; -ltermcap does the job on most systems, but if # you have ncurses installed you may want to change it to -lncurses. # On weird systems without a separate termcap library but with termcap # emulation in curses, you may need to set it to -lcurses. LIBS = -ltermcap # Extra libraries; uncomment this for Solaris, change it for other machines EXTRALIBS = -lsocket -lnsl # Uncomment this on non-POSIX BSD machines (NeXT, Sequent...) if you # have trouble compiling without it. # OPT1 = -DUSE_SGTTY # OPT2 = -DUSE_SIGVEC # Uncomment this if you have trouble compiling because of sigaction() # and USE_SIGVEC doesn't work: # OPT3 = -DNO_SIGACTION # Uncomment this if you want eight-bit-stripping on by default # OPT4 = -DSEVEN_BIT OPT5 = -DLIBDIR="\"$(LIBDIR)\"" OPTS = $(OPT1) $(OPT2) $(OPT3) $(OPT4) $(OPT5) #CFLAGS = -g -O -D__USE_FIXED_PROTOTYPES__ $(OPTS) #CFLAGS = -g -Wall -DDEBUG -D__USE_FIXED_PROTOTYPES__ $(OPTS) CFLAGS = -g -Wall -D__USE_FIXED_PROTOTYPES__ $(OPTS) #LDFLAGS = -g CC = gcc ROFF = nroff all: utalk utalk.cat: utalk.1 $(ROFF) -man utalk.1 > utalk.cat utalk: $(OBJS) $(CC) $(LDFLAGS) -o utalk $(OBJS) $(LIBS) $(EXTRALIBS) test: stest.o srdp.o $(CC) $(LDFLAGS) -o stest stest.o $(LIBS) $(EXTRALIBS) install: utalk utalk.1 # -strip utalk -umask 022; mkdir $(BINDIR) 2>/dev/null -umask 022; mkdir $(MANDIR) 2>/dev/null -umask 022; mkdir $(LIBDIR) 2>/dev/null -umask 022; mkdir $(MANDIR)/man1 2>/dev/null cp utalk $(BINDIR) chmod 755 $(BINDIR)/utalk cp utalk.1 $(MANDIR)/man1 chmod 644 $(MANDIR)/man1/utalk.1 cp utalk.help $(LIBDIR) chmod 644 $(LIBDIR)/utalk.help clean: rm -f $(OBJS) stest.o stest utalk core depend: makedepend -- $(CFLAGS) -- $(SRCS) utalk-1.0.1.beta.orig/LICENSE0100644000076600007650000004512306376753217015036 0ustar garabikatlas GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ------------------------------------------------------------------------------- Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. utalk-1.0.1.beta.orig/PROTOCOL0100644000076600007650000003774706376753221015225 0ustar garabikatlas protocol for utalk, the udp talk 1. A general-purpose sequenced packet low-level protocol, SRDP, for low-throughput connections, built over UDP. SRDP stands for Semi-Reliable Datagram Protocol. This defines a low-level protocol over which different types of data can be sent by building higher level protocols. This protocol will ensure the boundary-preserving transmission of packets, both orderless and sequenced. It allows the reading of packets as they arrive, potentially not in the order in which they were sent, and manages re-sending and acknowledging sequenced packets. The goals are to ensure the availability of data as soon as it arrives and the quick resending of lost data, appropriate for low-throughput connections in which quick response is important. Being built on UDP, the protocol doesn't provide any mechanism for an initial handshaking where both sides would agree on port numbers. This can be done with the help of some daemon, like the talk daemons. All packets belonging to the same connection are sent as UDP packets of non-null size from one machine to the other, always from the same port, always to the same port. Clients may be able to use the same port to handle various connections with different machines or different ports on the same remote machine. Packets are made of concatenated chunks (at least one of them). A chunk is the logical data unit; each chunk has a length (total length, counting the chunk header), is of a type (types are numbered), has a sequence number for some types, and has some contents (which may be empty). All chunks are prefaced by the protocol version and revision numbers, so clients can deal with incompatibilities; the low-level protocol layer will check that the version and revisions can talk to each other. There is an extra version number, for use by the higher level layer. All sequence numbers, type numbers, version numbers and lengths are transmitted in network-standard byte order (big endian), and are not padded to any length other than the one they are defined to be. Sequenced chunks are those for which the protocol handler needs to keep track of which ones have been received, sent, acknowledged, and expired. If several types of sequenced chunks are used, they are all sequenced together, starting with the sequence number 1. By convention, sequenced chunks will have even type numbers, and non-sequenced ones will have odd ones. Non-sequenced chunks either contain control data to manage the transmission of the sequenced chunks, or any other kind of data that does not need to be protected against packet loss. The layout of a sequenced chunk is: offset 0 - 8 bits - protocol version number (currently 1) offset 1 - 8 bits - protocol revision number (currently 0) offset 2 - 8 bits - high-level protocol number (ignored) offset 3 - 8 bits - type of the chunk (even) offset 4 - 32 bits - length of the chunk (at least 12) offset 8 - 32 bits - sequence number of the chunk offset 12 - variable - data The layout of a non-sequenced chunk is: offset 0 - 8 bits - protocol version number (currently 1) offset 1 - 8 bits - protocol revision number (currently 0) offset 2 - 8 bits - high-level protocol number (ignored) offset 3 - 8 bits - type of the chunk (odd) offset 4 - 32 bits - length of the chunk (at least 8) offset 8 - variable - data Chunk types 0xe0 to 0xff are reserved for the low-level protocol; all other types are left for the protocols built on it. The high-level programs using this protocol will read and generate data chunks of various types, sequenced or not, other than the control types defined by the low-level protocol itself. They can also set a number of general-purpose options like the values of the acknowledgement timeouts. They write chunks in order and receive them potentially not in order; on reception they also get the sequencing number, but the fact that these may not be consecutive if more than one sequenced chunk type is used makes it advisable to have the data contain enough information to deal with each chunk of data separately, independently of the order. The higher level application will also be able to request a resynch (i.e make the lowlevel library immediately send out a packet asking for any missing data and telling what the last received sequence number is) The protocol does not ensure that all sent packets will be received. Instead, if a sequenced chunk has never been received and other chunks with later sequence numbers are being received, the protocol will retry requesting the missing one a number of times, and will eventually give up on old chunks if the number of missing chunks becomes too large. The parameters to this are left as implementation decisions on the low-level protocol management libraries. Clients should ignore chunk types they don't recognize. The following types of chunks is initially defined; they are all non-sequenced, since they are used to control the sending of sequenced data chunks: SRDP_PING = 0xff - ping, requests a chunk back with the same data, and type SRDP_PINGREP SRDP_PINGREP = 0xfd - ping reply, returns the data SRDP_MISSLST = 0xfb - sends list of chunks that have never have been received, and that have a sequence number < to that of a received chunk; this list may (actually, has to, if it gets too long) be truncated, dropping the oldest missing chunks SRDP_CURRENT = 0xf9 - sends the number of the last sequenced chunk received so far (the one with the greatests sequence number) SRDP_OLDEST = 0xf7 - sends the number of the oldest sent sequenced chunk that is still available for resending SRDP_ALIVE = 0xf5 - tells the other side that the connection is not broken Clients should send chunks of type SRDP_MISSLST regularily but with a minimum interval between two of them, as long as the list of missing chunks is not empty. It is allowed to send packets of type SRDP_MISSLST with an empty missing chunk list, but it is not required. Clients should start sending chunks of type SRDP_CURRENT if they haven't received any packet for some time, and send them regularily but with a minimum interval between them. A chunk of type SRDP_CURRENT followed by a chunk of type SRDP_MISSLST counts as an acknowledgement that all chunks with a sequence number smaller than that specified on the SRDP_CURRENT chunk, and not explicitly mentioned in SRDP_MISSLST will not be requested again. Chunks of type SRDP_OLDEST should be sent as an answer to SRDP_MISSLST that request non-available chunks. Chunks of type SRDP_ALIVE should be sent periodically whenever nothing has been sent in a relatively long time. They contain nothing else than a chunk header. Failure to receive any kind of packets from the other end for a long period indicates a failure of the transport medium, which should be indicated to the high-level application, but should not cause the connection to be automatically considered broken. Format of a SRDP_MISSLST chunk: (0xfb) offset 0 - 8 bits - protocol version offset 1 - 8 bits - protocol revision offset 2 - 8 bits - high-level protocol version (ignored) offset 3 - 8 bits - type of the chunk : 0xfb offset 4 - 32 bits - length of the chunk (at least 8) offset 8 - 32 bits - most recent missing chunk sequence number offset 12 - 8 bits - number of additional missing chunks before that one (0 to 255) offset 13 - 32 bits - second most recent missing chunk sequence number offset 17 - 8 bits - number of additional missing chunks before that one (0 to 255) ... Format of a SRDP_CURRENT chunk: (0xf9) offset 0 - 8 bits - protocol version offset 1 - 8 bits - protocol revision offset 2 - 8 bits - high-level protocol version (ignored) offset 3 - 8 bits - type of the chunk : 0xf9 offset 4 - 32 bits - length of the chunk (12) offset 8 - 32 bits - greatest sequence number received so far Format of a SRDP_OLDEST chunk: (0xf7) offset 0 - 8 bits - protocol version offset 1 - 8 bits - protocol revision offset 2 - 8 bits - high-level protocol version (ignored) offset 3 - 8 bits - type of the chunk : 0xf7 offset 4 - 32 bits - length of the chunk (12) offset 8 - 32 bits - sequence number of the oldest sent chunk that is still available for resending In addition, the following types of semi-sequenced chunks are defined: SRDP_CLOSE = 0xfe - request termination of the connection SRDP_DROP = 0xfc - drop connection Those are actually not sent out with sequence numbers, but they are just repeated until acknowledged. When one end wants to gracefully shutdown the connection, it will send SRDP_CLOSE chunks periodically for a few seconds, or until it gets a SRDP_DROP back. In either case, it will consider the connection closed. The SRDP_DROP chunks sent as an answer must be alone in their packets. When one end wants to drop the connection without waiting, it will send 3 SRDP_DROP chunks and consider it closed. When one end gets a SRDP_DROP chunk, it will consider the connection closed. Format of a SRDP_CLOSE chunk: (request termination) offset 0 - 8 bits - protocol version offset 1 - 8 bits - protocol revision offset 2 - 8 bits - high-level protocol version (ignored) offset 3 - 8 bits - type of the chunk : 0xfe offset 4 - 32 bits - length of the chunk (at least 12) offset 8 - variable - error message encoded in the iso_8859_1 map (may be empty, may be ignored) Format of a 0xfe chunk: (terminate connection with error message) offset 0 - 8 bits - protocol version offset 1 - 8 bits - protocol revision offset 2 - 8 bits - high-level protocol version (ignored) offset 3 - 8 bits - type of the chunk : 0xfe offset 4 - 32 bits - length of the chunk (at least 12) offset 8 - variable - error message encoded in the iso_8859_1 character map (may be empty, may be ignored) 2. A high-level packet-oriented protocol for udp talk This protocol builds over the preceding one, and specifies the way a connection is established, as well as the way data is transmitted over the low-level protocol. The high-level protocol version must be set to 1 on all chunks; chunks with higher versions will be silently ignored, as will chunks with unknown types. To establish the connection, there are 3 ways: . To manually specify on one end a local port, and on the other a remote host and a remote port. . To have the starting side call the other side's talk daemon, with !(number) (without the parentheses) as the local username, where (number) is the local port number. . To have the starting side call the other side's talk daemon, with !(name) (without the parentheses) as the local username, where (name) is the usual login name of the caller, and leave an invite on the caller's daemon with the local port number. Clients can either connect to the standard BSD-type talk daemons on port 518 or the old-fashioned Sun ones on port 517, or both. Upon starting, clients should request a resynch, in case the other client has been running and sending data already. This protocol defines one sequenced type of chunk, with number UTALK_DATA = 0x02, containing data to be displayed: The layout of such a chunk will be: offset 0 - 8 bits - protocol version offset 1 - 8 bits - protocol revision offset 2 - 8 bits - high-level protocol number (currently 1) offset 3 - 8 bits - type of the chunk (2) offset 4 - 32 bits - length of the chunk (at least 10) offset 8 - 32 bits - sequence number of the chunk offset 12 - 16 bits - line number offset 14 - 16 bits - column number offset 16 - variable - data, encoded in the iso_8859_1 map, with a few characters and sequences to be interpreted specially The text in the talk is divided into logical lines of practically unlimited length, made of a number of characters followed by a virtually infinite number of virtual blanks, of which only those needed to reach the end of the physical line will be displayed as spaces. Real spaces (0x20) are considered solid characters, even at the end of lines. Clients are supposed to do word-wrap and/or line-wrap locally by changing to the next line after some column number. Lines and columns are numbered from 1 to 65535, with (1, 1) being the upper-left corner. Received chunks will move the cursor to the specified position, and then apply each of the characters in the data section. Packets with no data but a cursor position explicitly request that the cursor be moved to that position. The display routines must keep track of the mapping between physical lines and logical lines, keeping track of which logical lines are physically visible, and of how many physical lines each takes, and take it into account when initially positionning the cursor. When a logical line has to be extended into more than one physical line, and it is not the last physical line on the screen, the screen will have to be redrawn or text scrolled down. It is up to the client whether empty physical lines because of shortened logical lines should be reclaimed or left empty. It is up to the client whether packets with cursor addresses outside the physical screen should cause the display to scroll back or be ignored. This way clients able of scrollback and/or full editing of the previously typed data will be able to interoperate with those that are not, the latter displaying the changes only if they are operating on the physical screen. In data, the following values are treated specially: 0x00 - special character of displayed length zero 0xff - character introducing all utalk special sequences The defined special sequences are: 0xff 0x00 : replace the character at the given position, as well as anything after it on the same logical line, with virtual-blanks. reclaim physical lines if necessary, and move the cursor that position. 0xff 0x01 AA BB CC DD : set a new cursor position; AA,BB is the line number encoded as a 16-bit unsigned short in network order, and CC,DD is the column in the same encoding. 0xff 0x07 : beep the terminal (not storing anything, ignoring the line and column numbers) A character that has been replaced by a 0x00 must never be replaced by another character again. The operations of deleting a letter, word or full line are to be implemented in terms of 0xff 0x00 if possible, and in terms of replacing characters with 0x00 if not. All characters other these two can be displayed arbitrarily by the client, but have to be considered always to be of physical length one. Clients can deal with special characters (from 0x01 to 0x1f, from 0x7f to 0x9f) by either ignoring them (replacing them with spaces) or displaying them with some convention (using bold, or reverse-video). All characters with code >0x9f are considered to be iso-latin-1 encoded, and can be displayed either in iso-latin-1 or by converting them to some other local encoding, with or without accented characters. In addition to this, clients may locally recognize special keys to redraw the screen, request a resynch, toggle any modes, move the cursor, scroll back, etc. Another type of chunk, with number UTALK_TOPIC = 4 is also defined; it can be used by clients to send a string to be set as the "topic" of the conversation. Clients may ignore this chunk. Its layout is: offset 0 - 8 bits - protocol version offset 1 - 8 bits - protocol revision offset 2 - 8 bits - high-level protocol number (currently 1) offset 3 - 8 bits - type of the chunk (2) offset 4 - 32 bits - length of the chunk (at least 10) offset 8 - 32 bits - sequence number of the chunk offset 12 - variable - text of the topic, encoded in the iso_8859_1 map. utalk-1.0.1.beta.orig/comm.c0100644000076600007650000005313406376753223015126 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp Copyright (C) 1995 Roger Espel Llima comm.c Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "srdp.h" #include "globals.h" #include "rc.h" #include "screen.h" #include "util.h" #include "termcap.h" #include "kbd.h" #include "menu.h" #include "comm.h" static struct srdp_chunk *rc; static struct srdp_chunk *c; static struct srdp_chunk *topic_c; static int buffered_data = 0, buffered_len = 0; static int ofd = -1, nfd = -1; struct sockaddr_in osock, nsock; static int next_id = 1781, my_pid; static struct mesg last_invite; static struct sockaddr_in last_invite_addr; static int invite_sent = 0; static struct timeval send_when; /* valid only when buffered_data != 0 */ static struct timeval now; static int has_blocked = 1; static char errstr[200]; /* return host address in network order */ static srdp_u32 gethostaddr(char *s) { struct hostent *h; srdp_u32 tmp; if ((tmp = inet_addr(s)) == 0xffffffff) { if ((h = gethostbyname(s)) == NULL) cleanupexit(1,"Unknown host"); else return *(srdp_u32 *)(h->h_addr_list[0]); } else return tmp; return 0; /* so gcc will shut up */ } static void send_msg(struct sockaddr_in *to, struct mesg *msg) { int n; struct old_msg omsg; struct new_msg nmsg; memset(&omsg, 0, sizeof(omsg)); memset(&nmsg, 0, sizeof(nmsg)); switch(msg->daemon) { case ODAEMON: omsg.type = msg->type; if (msg->type == ANNOUNCE && strlen(msg->local_name) < 8) { omsg.l_name[0] = '!'; strncpy(&omsg.l_name[1], msg->local_name, NAME_SIZE-1); } else { strncpy(omsg.l_name, msg->local_name, NAME_SIZE); } strncpy(omsg.r_name, msg->remote_name, NAME_SIZE); strncpy(omsg.r_tty, msg->remote_tty, TTY_SIZE); /* omsg.addr = msg->addr; xxx */ omsg.id_num = htonl(msg->id); memcpy(&omsg.ctl_addr, &osock, sizeof(omsg.ctl_addr)); memcpy(&omsg.addr, &osock, sizeof(omsg.addr)); omsg.addr.sin_port = htons(users[1]->info.localport); omsg.ctl_addr.sin_family = omsg.addr.sin_family = htons(AF_INET); omsg.pid = htonl(my_pid); to->sin_port = htons(517); n = sendto(ofd, (char *)&omsg, sizeof(omsg),0, (struct sockaddr *)to, sizeof (*to)); if (n != sizeof(omsg)) cleanupexit(1, "sendto() failed"); break; case NDAEMON: nmsg.type = msg->type; if (msg->type == ANNOUNCE) { nmsg.l_name[0] = '!'; strncpy(&nmsg.l_name[1], msg->local_name, NAME_SIZE); } else { strncpy(nmsg.l_name, msg->local_name, NAME_SIZE); } strncpy(nmsg.r_name, msg->remote_name, NAME_SIZE); strncpy(nmsg.r_tty, msg->remote_tty, TTY_SIZE); /* nmsg.addr = msg->addr; xxx */ /* nmsg.ctl_addr = msg->ctl_addr; xxx */ nmsg.id_num = htonl(msg->id); memcpy(&nmsg.ctl_addr, &nsock, sizeof(nmsg.ctl_addr)); memcpy(&nmsg.addr, &nsock, sizeof(nmsg.addr)); nmsg.addr.sin_port = htons(users[1]->info.localport); nmsg.ctl_addr.sin_family = nmsg.addr.sin_family = htons(AF_INET); nmsg.vers = NTALKD_VERSION; nmsg.pid = htonl(my_pid); to->sin_port = htons(518); n = sendto(nfd, (char *)&nmsg, sizeof(nmsg),0, (struct sockaddr *)to, sizeof (*to)); if (n != sizeof(nmsg)) cleanupexit(1, "sendto() failed"); } } static void drain_socket(int fd) { struct sockaddr_in s; struct timeval tv; int r; size_t sz; char buf[200]; fd_set rfds; do { FD_ZERO(&rfds); FD_SET(fd, &rfds); tv.tv_sec = tv.tv_usec = 0; r = select(fd + 1, &rfds, NULL, NULL, &tv); if (r <= 0) break; sz = sizeof(s); r = recvfrom(fd, buf, 200, 0, (struct sockaddr *)&s, &sz); } while (r > 0); } static void parseanswer(struct answer *answ, void *org, int daemontype) { struct old_reply *oansw; struct new_reply *nansw; switch (daemontype) { case ODAEMON: oansw = (struct old_reply *)org; answ->type = oansw->type; answ->answer = oansw->answer; answ->id = ntohl(oansw->id_num); answ->addr = oansw->addr; break; case NDAEMON: nansw = (struct new_reply *)org; answ->type = nansw->type; answ->answer = nansw->answer; answ->id = ntohl(nansw->id_num); answ->addr = nansw->addr; break; } } static int recv_msg(struct sockaddr *from, struct answer *answ) { int n, fd; size_t sz; struct old_reply oansw; struct new_reply nansw; struct sockaddr_in sa; fd_set fds; struct timeval wait; fd = (answ->daemon == ODAEMON ? ofd : nfd); FD_ZERO(&fds); FD_SET(fd, &fds); wait.tv_sec = 2; wait.tv_usec = 0; n = select(fd+1, &fds, NULL, NULL, &wait); if (n <= 0) return -1; switch (answ->daemon) { case ODAEMON: sz = sizeof(sa); sa.sin_family = AF_INET; sa.sin_port = htons(0); sa.sin_addr.s_addr = htons(INADDR_ANY); n = recvfrom(ofd, (char *)&oansw, sizeof(oansw), 0, (struct sockaddr *)&sa, &sz); if (n<0) return -1; /* recv failed */ parseanswer(answ, &oansw, ODAEMON); break; case NDAEMON: sz = sizeof(sa); sa.sin_family = AF_INET; sa.sin_port = htons(0); sa.sin_addr.s_addr = htons(INADDR_ANY); n = recvfrom(nfd, (char *)&nansw, sizeof(nansw), 0, (struct sockaddr *)&sa, &sz); if (n<0) return -1; /* recv failed */ parseanswer(answ, &nansw, NDAEMON); break; } return 0; } static int find_daemon(char *me, char *him, srdp_u32 addr, char *machine, int *port) { int n, i, r; size_t sz; struct timeval wait; struct sockaddr_in d, rs; struct mesg msg; struct answer answ; struct old_reply oansw; struct new_reply nansw; fd_set rfds; int old = 0, new = 0; d.sin_family = PF_INET; d.sin_addr.s_addr = addr; drain_socket(ofd); drain_socket(nfd); if (him) strncpy(msg.remote_name, him, NAME_SIZE); else strcpy(msg.remote_name,"^_^"); if (me) strncpy(msg.local_name, me, NAME_SIZE); else strcpy(msg.local_name,"`&&'"); msg.remote_tty[0] = '\0'; msg.type = LOOK_UP; msg.id = next_id++; for (n = 0; n<5; n++) { msg.daemon = ODAEMON; send_msg(&d, &msg); msg.daemon = NDAEMON; send_msg(&d, &msg); FD_ZERO(&rfds); FD_SET(ofd, &rfds); FD_SET(nfd, &rfds); wait.tv_sec = 2; wait.tv_usec = 0; i = select(ofd > nfd ? ofd+1 : nfd+1, &rfds, NULL, NULL, &wait); if (i<0) cleanupexit(1, "select() failed"); if (i == 0) continue; while (i) { if (FD_ISSET(ofd, &rfds)) { sz = sizeof(struct sockaddr_in); rs.sin_family = AF_INET; rs.sin_port = htons(0); rs.sin_addr.s_addr = htons(INADDR_ANY); r = recvfrom(ofd, (char *)&oansw, sizeof(oansw), 0, (struct sockaddr *)&rs, &sz); if (r < 0) { if (errno != ECONNREFUSED && errno != EINTR) cleanupexit(1, "recvfrom() failed in find_daemon"); } else { old++; parseanswer(&answ, (void *)&oansw, ODAEMON); if (answ.answer == SUCCESS && port) *port = ntohs(answ.addr.sin_port); } } if (FD_ISSET(nfd, &rfds)) { sz = sizeof(struct sockaddr_in); rs.sin_family = AF_INET; rs.sin_port = htons(0); rs.sin_addr.s_addr = htons(INADDR_ANY); r = recvfrom(nfd, (char *)&nansw, sizeof(nansw), 0, (struct sockaddr *)&rs, &sz); if (r < 0) { if (errno != ECONNREFUSED && errno != EINTR) cleanupexit(1, "recvfrom() failed again in find_daemon"); } else { new++; parseanswer(&answ, (void *)&nansw, NDAEMON); if (answ.answer == SUCCESS && port) *port = ntohs(answ.addr.sin_port); } } if (old && new) break; FD_ZERO(&rfds); FD_SET(ofd, &rfds); FD_SET(nfd, &rfds); wait.tv_sec = 0; wait.tv_usec = 500000L; i = select(ofd > nfd ? ofd+1 : nfd+1, &rfds, NULL, NULL, &wait); if (i<0) cleanupexit(1, "select() failed again"); } if (new && old) return BOTH; else if (new) return NDAEMON; else return ODAEMON; } sprintf(errstr, "No talk daemon on %s\n", machine); cleanupexit(1, errstr); return 0; /* so gcc will shut up */ } /* initializations that need to be done only if using the talk daemons */ static void init_talkd(char *h, int *port) { char *s, *t; size_t len = sizeof(struct sockaddr_in); char uh[256]; strncpy(uh, h, 255); uh[254] = 0; if ((s = strchr(uh, '#')) != NULL) { *s++ = 0; strncpy(users[1]->tty, s, 16); } else { users[1]->tty[0] = 0; } h = resolve_alias(uh); for (t = users[1]->name, s = h; *s && *s != '@' ;) *t++ = *s++; *t = 0; if (*s == 0) strncpy(users[1]->hostname, users[0]->hostname, 254); else strncpy(users[1]->hostname, ++s, 254); users[1]->hostaddr = gethostaddr(users[1]->hostname); ofd = socket(PF_INET, SOCK_DGRAM, 0); if (ofd < 0) cleanupexit(1, "no more sockets"); osock.sin_family = AF_INET; osock.sin_addr.s_addr = htonl(INADDR_ANY); osock.sin_port = 0; if (bind(ofd, (struct sockaddr *)&osock, sizeof(osock)) < 0) cleanupexit(1, "can't bind() socket"); if (getsockname(ofd, (struct sockaddr *)&osock, &len) < 0) cleanupexit(1, "can't find out socket port"); osock.sin_addr.s_addr = users[0]->hostaddr; nfd = socket(PF_INET, SOCK_DGRAM, 0); if (nfd < 0) cleanupexit(1, "no more sockets"); nsock.sin_family = AF_INET; nsock.sin_addr.s_addr = htonl(INADDR_ANY); nsock.sin_port = 0; if (bind(nfd, (struct sockaddr *)&nsock, sizeof(nsock)) < 0) cleanupexit(1, "can't bind() socket"); if (getsockname(nfd, (struct sockaddr *)&nsock, &len) < 0) cleanupexit(1, "can't find out socket port"); nsock.sin_addr.s_addr = users[0]->hostaddr; if (port) { users[0]->daemon = find_daemon(NULL, NULL, users[0]->hostaddr, users[0]->hostname, NULL); users[1]->daemon = find_daemon(users[0]->name, users[1]->name, users[1]->hostaddr, users[1]->hostname, port); if (users[1]->daemon == BOTH) { users[1]->daemon = users[0]->daemon; if (users[1]->daemon == BOTH) users[1]->daemon = NDAEMON; } users[1]->ourdaemon = users[0]->daemon; if (users[1]->ourdaemon == BOTH) users[1]->ourdaemon = users[1]->daemon; } else { users[1]->daemon = find_daemon(users[0]->name, users[1]->name, users[1]->hostaddr, users[1]->hostname, port); if (users[1]->daemon == BOTH) users[1]->daemon = NDAEMON; } } static void ring_user(struct user *u, int raw) { int i; struct mesg msg; struct answer answ; struct sockaddr_in d; msg.id = next_id++; drain_socket(u->daemon == ODAEMON ? ofd : nfd); for (i = 0; i<4; i++) { strncpy(msg.remote_tty, u->tty, TTY_SIZE); msg.remote_tty[TTY_SIZE-1] = 0; msg.type = ANNOUNCE; strncpy(msg.remote_name, u->name, NAME_SIZE); if (raw) sprintf(msg.local_name, "%d", u->info.localport); else strncpy(msg.local_name, users[0]->name, NAME_SIZE); answ.daemon = msg.daemon = u->daemon; d.sin_family = PF_INET; d.sin_addr.s_addr = u->hostaddr; send_msg(&d, &msg); if (recv_msg((struct sockaddr *)&d, &answ) != 0) continue; switch(answ.answer) { case SUCCESS: return; case NOT_HERE: cleanupexit(1, "User not logged in"); return; case FAILED: cleanupexit(1, "Unknown talk daemon failure"); return; case MACHINE_UNKNOWN: cleanupexit(1, "Remote talk daemon can't find our hostname"); return; case PERMISSION_DENIED: cleanupexit(1, "User refusing messages"); return; default: cleanupexit(1, "Unknown talkd protocol error"); return; } return ; } cleanupexit(1, "Remote daemon not responding"); } static void leave_invite(struct user *u) { int i; struct mesg msg; struct answer answ; struct sockaddr_in d; msg.id = next_id; next_id += 5; drain_socket(u->ourdaemon == ODAEMON ? ofd : nfd); for (i = 0; i<4; i++) { msg.remote_tty[0] = 0; msg.type = LEAVE_INVITE; strncpy(msg.remote_name, u->name, NAME_SIZE); strncpy(msg.local_name, users[0]->name, NAME_SIZE); answ.daemon = msg.daemon = u->ourdaemon; d.sin_family = PF_INET; d.sin_addr.s_addr = users[0]->hostaddr; last_invite = msg; last_invite_addr = d; invite_sent++; send_msg(&d, &msg); if (recv_msg((struct sockaddr *)&d, &answ) != 0) continue; last_invite.id = answ.id; return ; } cleanupexit(1, "Local daemon not responding"); } void quick_clear_invite(void) { if (invite_sent) { last_invite.type = DELETE; send_msg(&last_invite_addr, &last_invite); send_msg(&last_invite_addr, &last_invite); } } static void clear_invite(void) { int i; struct answer answ; struct sockaddr_in d; struct mesg msg; if (invite_sent) { for (i = 0; i<4; i++) { msg = last_invite; msg.type = DELETE; answ.daemon = msg.daemon; d = last_invite_addr; send_msg(&d, &msg); if (recv_msg((struct sockaddr *)&d, &answ) != 0) continue; invite_sent = 0; } } } void init_comm(int raw, char *h, int p, char *uh) { static char tmp[256]; time_t when; struct timeval tv; int client = 0, port = 0; struct passwd *pw; next_id = my_pid = getpid(); if ((pw = getpwuid(getuid())) != NULL) strncpy(users[0]->name, pw->pw_name, 8); else strcpy(users[0]->name, "unknown"); if (gethostname(users[0]->hostname, 254) < 0) strcpy(users[0]->hostname, "unknown"); users[0]->hostaddr = gethostaddr(users[0]->hostname); rc = (struct srdp_chunk *)mymalloc(READBUFSIZE); c = (struct srdp_chunk *)mymalloc(OUTBUFSIZE); topic_c = (struct srdp_chunk *)mymalloc(300); memset((char *)&users[1]->info, 0, sizeof(struct srdp_info)); users[1]->hostname[0] = 0; if (raw == 0) { init_talkd(uh, &port); client = (port != 0); } else if (raw == -1) { init_talkd(uh, NULL); client = 0; } else { port = p; client = (raw == 2); if (client) { sprintf(users[1]->name, "%d", port); if (h && *h) strncpy(users[1]->hostname, h, 254); else { strcpy(users[1]->hostname, users[0]->hostname); h = users[0]->hostname; } } } if (!client && port) users[1]->info.localport = (u_short)port; else users[1]->info.localport = 0; if (client) users[1]->info.remoteport = (u_short)port; else users[1]->info.remoteport = 0; if (client) users[1]->info.remote.s_addr = (raw>0 ? gethostaddr(h) : users[1]->hostaddr); else users[1]->info.remote.s_addr = htonl(INADDR_ANY); users[1]->info.curr_1_time = 4; users[1]->info.curr_nxt_time = 5; users[1]->info.miss_time = 3; users[1]->info.oldest_time = 5; users[1]->info.alive_time = 8; users[1]->info.broken_time = 30; if (srdp_open(&users[1]->info)<0) cleanupexit(1, "Can't open connection"); connected++; users[1]->info.flags|= SRDP_FL_SELRETURN; setstatus(users[1], "waiting for connection..."); gotoxy(1, 2); flush_term(); if (!client && raw <= 0) ring_user(users[1], raw); do { if (!client && !raw) leave_invite(users[1]); when = time(NULL) + 28; tv.tv_sec = 28; tv.tv_usec = 0; do { srdp_select(1, NULL, NULL, NULL, &tv); if (users[1]->info.flags & SRDP_FL_RECVD) break; tv.tv_sec = when - time(NULL); tv.tv_usec = 0; } while (tv.tv_sec > 0 && tv.tv_sec < 30); } while ((users[1]->info.flags & SRDP_FL_RECVD) == 0); srdp_synch(&users[1]->info); clear_invite(); if (raw>0 && !client) { sprintf(users[1]->name, "%d", users[1]->info.remoteport); strncpy(users[1]->hostname, inet_ntoa(users[1]->info.remote), 254); } sprintf(tmp, "%s@%s", users[1]->name, users[1]->hostname); setstatus(users[1], tmp); } void gettime_cached(struct timeval *tv) { static struct timeval ltv; if (!has_blocked) *tv = ltv; else { gettimeofday(tv, NULL); ltv = *tv; has_blocked = 0; } } void flush_buf(void) { if (buffered_data) { c->u.sequenced.len = htonl(16+buffered_len); c->u.sequenced.hi_version = UTALK_REVISION; c->u.sequenced.type = UTALK_DATA; srdp_write(&users[1]->info, c); buffered_data = buffered_len = 0 ; } } void main_loop(void) { fd_set fds; unsigned char kbd_buf[256], *cp; int r; srdp_u32 chunksize; struct timeval tmv, *tmvp; in_main_loop = 1; while (!mustdie) { doflags(users[0]); doflags(users[1]); flush_term(); if (buffered_data) { gettime_cached(&now); if (now.tv_sec>send_when.tv_sec || (now.tv_sec == send_when.tv_sec && now.tv_usec >= send_when.tv_usec)) { flush_buf(); tmvp = NULL; } else { tmv.tv_sec = send_when.tv_sec-now.tv_sec; if (now.tv_usec>send_when.tv_usec) { tmv.tv_usec = 1000000+send_when.tv_usec-now.tv_usec; tmv.tv_sec--; } else tmv.tv_usec = send_when.tv_usec-now.tv_usec; tmvp = &tmv; } } else tmvp = NULL; FD_ZERO(&fds); FD_SET(0, &fds); r = srdp_select(1, &fds, NULL, NULL, tmvp); has_blocked = 1; if (buffered_data) { gettime_cached(&now); if (now.tv_sec>send_when.tv_sec || (now.tv_sec == send_when.tv_sec && now.tv_usec >= send_when.tv_usec)) flush_buf(); } if (mustredisplay) { mustredisplay = 0; redraw(); } if (winch) { int oldlines = lines; cols = newcols; lines = newlines; winch = 0; resize_termcap_screen(oldlines); cannotrun = (cols<4 || lines 0) keyboard(*cp++); } } if ((users[1]->info.flags&SRDP_FL_CLOSED) != 0) { connected = 0; cleanupexit(1, "Connection closed"); } if (users[1]->info.flags&SRDP_FL_READY) { srdp_read(&users[1]->info, rc, 2000); chunksize = ntohl(rc->u.sequenced.len); if (rc->u.sequenced.type == UTALK_DATA) { srdp_u16 line, col; srdp_u32 seq; struct logical_line *l; unsigned char *s, *t; seq = ntohl(rc->u.sequenced.seq); memcpy((char *)&line, ((char *)rc)+12, 2); memcpy((char *)&col, ((char *)rc)+14, 2); s = ((unsigned char *)rc)+16; t = s + (chunksize - 16); line = ntohs(line); col = ntohs(col); l = find_lline(users[1], line, seq); if (s < t) { while (s < t) { if ((*s) == 0xff) { s++; if (*s == 0) { shorten_lline(users[1], l, seq, col-1); s++; } else if (*s == 1) { s++; memcpy((char *)&line, (char *)s, 2); s += 2; memcpy((char *)&col, (char *)s, 2); s += 2; line = ntohs(line); col = ntohs(col); l = find_lline(users[1], line, seq); if (s >= t) set_cursor(users[1], l, seq, col); } else if (*s == 7) { beep(); s++; } } else write_char(users[1], l, seq, col++, *(s++)); } } else set_cursor(users[1], l, seq, col); } else if (rc->u.sequenced.type == UTALK_TOPIC) { unsigned char *s; s = ((unsigned char *)rc)+12; if (chunksize < 300) { s[chunksize - 12] = '\0'; settopic(s); } } } } } /* if the 2st char in s is a 0xff, the whole thing is assumed to be only control chars that do not add to the nextcol counter! also, do not pass the 0xff,0x01 sequence thru c_write_string, use the line,col params instead */ void c_write_string(int line, int col, unsigned char *s, int len) { srdp_u16 cc, ll; static int nextcol = -1, goodline = -1; if (buffered_len + len>SENDAFTER) flush_buf(); cc = htons((srdp_u16)(col)); ll = htons((srdp_u16)(line)); if (!buffered_len) { memcpy(((char *)c)+12, (char *)&ll, 2); memcpy(((char *)c)+14, (char *)&cc, 2); } else { if (line != goodline || col != nextcol) { *(((char *)c) + 16 + buffered_len++) = 0xff; *(((char *)c) + 16 + buffered_len++) = 0x01; memcpy((((char *)c)+16+buffered_len), (char *)&ll, 2); buffered_len += 2; memcpy((((char *)c)+16+buffered_len), (char *)&cc, 2); buffered_len += 2; } } if (len > 0 && *s == 0xff) nextcol = col; else nextcol = col+len; goodline = line; if (len > 0) { memcpy((((char *)c)+16+buffered_len), (char *)s, len); buffered_len += len; } if (!buffered_data) { send_when = now; send_when.tv_usec += MAXBUFDELAY; if (send_when.tv_usec>1000000) { send_when.tv_usec -= 1000000; send_when.tv_sec++; } buffered_data = 1; } } void c_write_char(int line, int col, unsigned char ch) { c_write_string(line, col, &ch, 1); } void c_set_cursor(int line, int col) { c_write_string(line, col, NULL, 0); } void c_shorten_lline(int line, int len) { unsigned char wb[2]; wb[0] = 0xff; wb[1] = 0; c_write_string(line, len+1, wb, 2); } void c_send_beep(int line, int col) { unsigned char wb[2]; wb[0] = 0xff; wb[1] = 0; c_write_string(line, col, wb, 2); } void c_resync(void) { flush_buf(); srdp_synch(&users[1]->info); } void c_close(void) { flush_buf(); srdp_drop(&users[1]->info, ""); } void c_send_topic(char *s) { int len; len = strlen(s); if (len > 255) s[255] = '\0'; topic_c->u.sequenced.len = htonl(12+len); topic_c->u.sequenced.hi_version = UTALK_REVISION; topic_c->u.sequenced.type = UTALK_TOPIC; memcpy(((char *)topic_c)+12, s, len+1); srdp_write(&users[1]->info, topic_c); } utalk-1.0.1.beta.orig/functions.c0100644000076600007650000006566206376753226016217 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp by Roger Espel Llima functions.c This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include #include #include "globals.h" #include "termcap.h" #include "screen.h" #include "util.h" #include "comm.h" #include "functions.h" #include "struct.h" #include "kbd.h" #include "menu.h" static next_pos = 1; /* for my window */ struct logical_line *current_lline; /* lline with the cursor */ /* conventions: no nulls at the end of a logical line (use shorten_lline instead of overwriting with nulls), next_pos never points to a null */ #define U0 users[0] #define L current_lline int active_window = -1; /* -1 = mine, read-write 0 = mine, read-only 1 = first other, read-only ... */ static int last_find_char = 0; static ftype last_find_function = NULL, reverse_find_function = NULL; struct function the_functions[] = { { "self-insert", f_self_insert }, { "insert-in-place", f_insert_in_place }, { "tab", f_tab }, { "new-line", f_new_line }, { "delete", f_delete }, { "delete-end-of-line", f_delete_end_of_line }, { "delete-beginning-of-line", f_delete_beginning_of_line }, { "delete-line", f_delete_line }, { "delete-word", f_delete_word }, { "delete-end-of-word", f_delete_end_of_word }, { "backspace", f_backspace }, { "backspace-word", f_backspace_word }, { "backward", f_backward }, { "forward", f_forward }, { "backward-word", f_backward_word }, { "forward-word", f_forward_word }, { "end-of-word", f_end_of_word }, { "beginning-of-line", f_beginning_of_line }, { "end-of-line", f_end_of_line }, { "nop", f_nop }, { "beep", f_beep }, { "up", f_up }, { "down", f_down }, { "up-page", f_up_page }, { "down-page", f_down_page }, { "up-half-page", f_up_half_page }, { "down-half-page", f_down_half_page }, { "top-of-screen", f_top_of_screen }, { "middle-of-screen", f_middle_of_screen }, { "bottom-of-screen", f_bottom_of_screen }, { "top-or-up-page", f_top_or_up_page }, { "bottom-or-down-page", f_bottom_or_down_page }, { "top", f_top }, { "bottom", f_bottom }, { "vi-goto-line", f_vi_goto_line }, { "redisplay", f_redisplay }, { "resynch", f_resynch }, { "next-window", f_next_window }, { "vi-insert-mode", f_vi_insert_mode }, { "vi-command-mode", f_vi_command_mode }, { "emacs-mode", f_emacs_mode }, { "quit", f_quit }, { "vi-escape", f_vi_escape }, { "vi-add", f_vi_add }, { "vi-add-at-end-of-line", f_vi_add_at_end_of_line }, { "vi-insert-at-beginning-of-line", f_vi_insert_at_beginning_of_line }, { "vi-open", f_vi_open }, { "vi-open-above", f_vi_Open }, { "vi-replace-char", f_vi_replace_char }, { "vi-find-char", f_vi_find_char }, { "vi-reverse-find-char", f_vi_reverse_find_char }, { "vi-till-char", f_vi_till_char }, { "vi-reverse-till-char", f_vi_reverse_till_char }, { "vi-repeat-find", f_vi_repeat_find }, { "vi-reverse-repeat-find", f_vi_reverse_repeat_find }, { "vi-delete-find-char", f_vi_delete_find_char }, { "vi-delete-reverse-find-char", f_vi_delete_reverse_find_char }, { "vi-delete-till-char", f_vi_delete_till_char }, { "vi-delete-reverse-till-char", f_vi_delete_reverse_till_char }, { "vi-flip-case", f_vi_flip_case }, { "quote-char", f_quote_char }, { "do-help", f_do_help }, { "set-topic", f_set_topic }, { "do-command", f_do_command }, { "test-menu", f_test_menu }, /* xxx */ { "test-entry", f_test_entry }, /* xxx */ { "test-selection", f_test_selection }, /* xxx */ { "", (ftype)0 } }; static int check_write(void) { if (active_window != -1) { beep(); return 0; } else return 1; } static int skip_nulls(struct logical_line *l, int pos0, int delta) { /* assumes l is the line next_pos applies to, and skips nulls to the right if delta == 1, to the left if delta == -1 */ int r = pos0; while (l->chars[r] == 0 && r+delta <= l->length && r+delta>0) r += delta; return r; } /* the argument to all f_* functions is the last char in the sequence */ void f_new_line(unsigned char c) { if (!check_write()) return; L = find_lline(U0, L->number + 1, 0); next_pos = skip_nulls(L, 1, 1); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } void f_self_insert(unsigned char c) { struct logical_line *l1; int pl, ppos, nl, nl0; if (!check_write()) return; if (c == 0 || c == 0xff || next_pos>0xfffc) beep(); else { if (c == 7) { beep(); c_send_beep(L->number, next_pos); } else { if (wordwrap && L->next == NULL && L->length == next_pos - 1) { pl = find_pline(U0, L); ppos = get_plength(U0, &pl, next_pos); if (ppos > cols) { if (c == ' ') { f_new_line(c); return; } next_pos = 1; l1 = find_lline(U0, L->number + 1, 0); nl = L->length; while (nl>0 && L->chars[nl] != ' ' && L->chars[nl] != 0xff) nl--; if (nl>0) { nl0 = nl+1; nl = skip_nulls(L, nl0, 1); if (L->chars[nl] != ' ' && L->chars[nl] != 0xff) { for (; nl <= L->length; nl++) { if (L->chars[nl] != 0 && L->chars[nl] != 0xff) { write_char(U0, l1, 0, next_pos, L->chars[nl]); c_write_char(l1->number, next_pos, L->chars[nl]); } if (L->chars[nl] != 0) next_pos++; } shorten_lline(U0, L, 0, nl0-1); c_shorten_lline(L->number, nl0-1); } } L = l1; } } write_char(U0, L, 0, next_pos, c); c_write_char(L->number, next_pos, c); next_pos = skip_nulls(L, ++next_pos, 1); } } } void f_insert_in_place(unsigned char c) { if (!check_write()) return; if (c == 0 || c == 0xff || next_pos>0xfffc) beep(); else { if (c == 7) { beep(); c_send_beep(L->number, next_pos); } else { write_char(U0, L, 0, next_pos, c); c_write_char(L->number, next_pos, c); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } } } void f_tab(unsigned char c) { int pl, ppos, toadd; if (!check_write()) return; if (next_pos>0xfff8) beep(); else { pl = find_pline(U0, L); ppos = get_plength(U0, &pl, next_pos); toadd = 9+((ppos-1)&0xfff8)-ppos; for (; toadd>0; toadd--) { next_pos++; if (next_pos <= L->length) next_pos = skip_nulls(L, next_pos, +1); } set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } } void f_delete(unsigned char c) { int npos; if (!check_write()) return; if (next_pos == L->length) { if (next_pos>1) { npos = skip_nulls(L, next_pos-1, -1); if (L->chars[npos] == 0) npos = 0; } else npos = 0; next_pos = npos+1; shorten_lline(U0, L, 0, npos); c_shorten_lline(L->number, npos); } else if (next_poslength) { write_char(U0, L, 0, next_pos, 0); c_write_char(L->number, next_pos, 0); next_pos = skip_nulls(L, next_pos, 1); } } static char nulls[512]; static void delete_till(struct logical_line *l, int till) { int nl, nl0, n; if (till < next_pos) { n = till; till = next_pos - 1; next_pos = n; if (next_pos < 1) next_pos = 1; } if (till >= l->length) { f_delete_end_of_line(0); } else { nl0 = next_pos; nl = skip_nulls(l, till, -1); n = nl - nl0 + 1; memset(nulls, 0, 512); while (n > 0) { if (n > 512) { c_write_string(l->number, nl0, nulls, 512); nl0 += 512; n -= 512; } else { c_write_string(l->number, nl0, nulls, n); nl0 += n; n = 0; } } for (; next_pos <= nl; next_pos++) write_char(U0, l, 0, next_pos, 0); next_pos = skip_nulls(l, next_pos, 1); set_cursor(U0, l, 0, next_pos); c_set_cursor(l->number, next_pos); } } void f_delete_beginning_of_line(unsigned char c) { if (!check_write()) return; if (next_pos > skip_nulls(L, 1, 1)) delete_till(L, 1); } void f_delete_end_of_line(unsigned char c) { int npos; if (!check_write()) return; if (next_pos <= L->length+1) { if (next_pos>1) { npos = skip_nulls(L, next_pos-1, -1); if (L->chars[npos] == 0) npos = 0; } else npos = 0; next_pos = npos+1; shorten_lline(U0, L, 0, npos); c_shorten_lline(L->number, npos); } } void f_delete_line(unsigned char c) { if (!check_write()) return; if (L->length) { next_pos = 1; shorten_lline(U0, L, 0, 0); c_shorten_lline(L->number, 0); } } void f_backspace(unsigned char c) { int opos, npos; if (!check_write()) return; if (next_pos>1) { if (L->length == next_pos-1) { opos = skip_nulls(L, next_pos-1, -1); if (opos>1) { npos = skip_nulls(L, opos-1, -1); if (L->chars[npos] == 0) npos = 0; } else npos = 0; shorten_lline(U0, L, 0, npos); c_shorten_lline(L->number, npos); next_pos = npos+1; } else { npos = skip_nulls(L, next_pos-1, -1); if (L->chars[npos] != 0) { write_char(U0, L, 0, npos, 0); c_write_char(L->number, npos, 0); } } } } void f_backspace_word(unsigned char c) { int nl, nl0, n; if (!check_write()) return; if (next_pos>1) { if (L->length == next_pos-1) { nl = L->length; while (nl>0 && (L->chars[nl] == ' ' || L->chars[nl] == 0xff || L->chars[nl] == 0)) nl--; while (nl>0 && L->chars[nl] != ' ' && L->chars[nl] != 0xff) nl--; shorten_lline(U0, L, 0, nl); c_shorten_lline(L->number, nl); next_pos = nl+1; } else { nl = nl0 = next_pos-1; while (nl>0 && (L->chars[nl] == ' ' || L->chars[nl] == 0xff || L->chars[nl] == 0)) nl--; while (nl>0 && L->chars[nl] != ' ' && L->chars[nl] != 0xff) nl--; nl = skip_nulls(L, nl+1, 1); nl0 = skip_nulls(L, nl0, -1); n = nl0-nl+1; memset(nulls, 0, 512); while (n > 0) { if (n > 512) { n -= 512; c_write_string(L->number, nl0-n+1, nulls, 512); } else { c_write_string(L->number, nl0-n+1, nulls, n); n = 0; } } for (; nl <= nl0; nl++) write_char(U0, L, 0, nl, 0); } } } void f_delete_word(unsigned char c) { int n; if (!check_write()) return; n = next_pos; if (n <= L->length) { while (n <= L->length && L->chars[n] != ' ' && L->chars[n] != 0xff) n++; while (n <= L->length+1 && (L->chars[n] == ' ' || L->chars[n] == 0xff || L->chars[n] == 0)) n++; delete_till(L, n-1); } } void f_delete_end_of_word(unsigned char c) { int n; if (!check_write()) return; n = next_pos + 1; if (n <= L->length) { while (n <= L->length && (L->chars[n] == ' ' || L->chars[n] == 0xff || L->chars[n] == 0)) n++; while (n <= L->length && L->chars[n] != ' ' && L->chars[n] != 0xff) n++; delete_till(L, n-1); } } void f_backward(unsigned char c) { int npos; if (!check_write()) return; if (vi_prefix) { npos = next_pos; while (vi_prefix > 0) { vi_prefix--; npos = skip_nulls(L, npos-1, -1); if (npos <= 1) break; } } else npos = skip_nulls(L, next_pos-1, -1); if (npos>0 && L->chars[npos] != 0) { next_pos = npos; set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } } void f_forward(unsigned char c) { int npos; if (!check_write()) return; if (vi_prefix) { npos = next_pos; while (vi_prefix > 0) { vi_prefix--; npos = skip_nulls(L, npos+1, 1); if (npos > L->length) break; } } else npos = skip_nulls(L, next_pos+1, 1); if (npos <= L->length+1) { next_pos = npos; set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } } void f_backward_word(unsigned char c) { int nl; if (!check_write()) return; if (next_pos>1) { nl = next_pos-1; while (nl>0 && (L->chars[nl] == ' ' || L->chars[nl] == 0xff || L->chars[nl] == 0)) nl--; while (nl>0 && L->chars[nl] != ' ' && L->chars[nl] != 0xff) nl--; next_pos = skip_nulls(L, nl+1, 1); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } } void f_forward_word(unsigned char c) { int nl; if (!check_write()) return; if (next_pos <= L->length) { nl = next_pos; while (nl <= L->length && L->chars[nl] != ' ' && L->chars[nl] != 0xff) nl++; while (nl <= L->length && (L->chars[nl] == ' ' || L->chars[nl] == 0xff || L->chars[nl] == 0)) nl++; next_pos = nl; set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } } void f_end_of_word(unsigned char c) { int nl; if (!check_write()) return; if (next_pos <= L->length) { nl = next_pos + 1; while (nl <= L->length && (L->chars[nl] == ' ' || L->chars[nl] == 0xff || L->chars[nl] == 0)) nl++; while (nl <= L->length && L->chars[nl] != ' ' && L->chars[nl] != 0xff) nl++; next_pos = skip_nulls(L, nl-1, -1); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } } void f_beginning_of_line(unsigned char c) { if (!check_write()) return; if (next_pos>1) { next_pos = skip_nulls(L, 1, 1); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } } void f_end_of_line(unsigned char c) { if (!check_write()) return; if (next_pos <= L->length) { next_pos = L->length+1; set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } } void f_vi_insert_at_beginning_of_line(unsigned char c) { if (!check_write()) return; if (next_pos>1) { next_pos = skip_nulls(L, 1, 1); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } f_vi_insert_mode(c); } void f_vi_add_at_end_of_line(unsigned char c) { if (!check_write()) return; if (next_pos <= L->length) { next_pos = L->length+1; set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } f_vi_insert_mode(c); } void f_up(unsigned char c) { struct logical_line *ol; int pl, ppos, s; if (active_window == -1) { if (L->number > 1) { ol = L; pl = find_pline(U0, ol); ppos = get_plength(U0, &pl, next_pos); if (vi_prefix) { while (vi_prefix > 0 && L->number > 1) { vi_prefix--; L = L->prev; } } else { L = ol->prev; } next_pos = 1; s = skip_nulls(L, 1, 1); while (--ppos>0) if (s <= L->length) s = skip_nulls(L, ++s, 1); else break; if (s > L->length) s = L->length; if (s == 0) s = 1; set_cursor(U0, L, 0, s); c_set_cursor(L->number, s); next_pos = s; } } else if (users[active_window]->first_visible_pline>1) scroll_to_pline(users[active_window], users[active_window]->first_visible_pline-1); } void f_down(unsigned char c) { struct logical_line *ol; int pl, ppos, s; if (active_window == -1) { if (L->number < U0->last_lline->number) { ol = L; pl = find_pline(U0, ol); ppos = get_plength(U0, &pl, next_pos); if (vi_prefix) { while (vi_prefix > 0 && L->number < U0->last_lline->number) { vi_prefix--; L = L->next; } } else { L = ol->next; } next_pos = 1; s = skip_nulls(L, 1, 1); while (--ppos>0) if (s <= L->length) s = skip_nulls(L, ++s, 1); else break; if (s > L->length) s = L->length; if (s == 0) s = 1; set_cursor(U0, L, 0, s); c_set_cursor(L->number, s); next_pos = s; } } else if (users[active_window]->first_visible_pline< users[active_window]->nplines-1) scroll_to_pline(users[active_window], users[active_window]->first_visible_pline+1); } static void move_by_plines(struct logical_line *l, int pl, int n) { pl += n; if (pl < 1) pl = 1; if (pl > U0->nplines) pl = U0->nplines; if (U0->plines[pl].line != l || next_pos != skip_nulls(l, U0->plines[pl].offset, 1)) { L = U0->plines[pl].line; next_pos = skip_nulls(L, U0->plines[pl].offset, 1); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } } static void scroll_by_plines(int n) { int pl; pl = users[active_window]->first_visible_pline + n; if (pl < 1) pl = 1; if (pl >= users[active_window]->nplines) pl = users[active_window]->nplines - 1; scroll_to_pline(users[active_window], pl); } static void move_to_top(void) { L = U0->plines[U0->first_visible_pline].line; next_pos = skip_nulls(L, U0->plines[U0->first_visible_pline].offset, 1); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } static void move_to_middle(void) { int pl; pl = U0->first_visible_pline + (U0->winsize / 2); if (pl > U0->nplines) pl = U0->nplines; L = U0->plines[pl].line; next_pos = skip_nulls(L, U0->plines[pl].offset, 1); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } static void move_to_bottom(void) { int pl; pl = U0->first_visible_pline + U0->winsize - 1; if (pl > U0->nplines) pl = U0->nplines; L = U0->plines[pl].line; next_pos = skip_nulls(L, U0->plines[pl].offset, 1); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } void f_top_of_screen(unsigned char c) { if (!check_write()) return; move_to_top(); } void f_middle_of_screen(unsigned char c) { if (!check_write()) return; move_to_middle(); } void f_bottom_of_screen(unsigned char c) { if (!check_write()) return; move_to_bottom(); } static void goto_line_number(int n) { if (n < 1) n = 1; if (active_window == -1) { if (n > U0->last_lline->number) n = U0->last_lline->number; L = find_lline(U0, n, 0); next_pos = skip_nulls(L, 1, 1); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } else { if (n > users[active_window]->nplines - users[active_window]->winsize + 1) n = users[active_window]->nplines - users[active_window]->winsize + 1; if (n < 1) n = 1; scroll_to_pline(users[active_window], n); } } void f_top(unsigned char c) { goto_line_number(1); } void f_bottom(unsigned char c) { if (active_window == -1) goto_line_number(U0->last_lline->number); else goto_line_number(users[active_window]->nplines); } void f_vi_goto_line(unsigned char c) { if (vi_prefix == 0) f_bottom(c); else goto_line_number(vi_prefix); } void f_top_or_up_page(unsigned char c) { int pl; if (active_window == -1) { pl = find_pline(U0, L); while (plnplines && skip_nulls(L, U0->plines[pl].offset, 1) < next_pos && U0->plines[pl+1].line == L) pl++; if (U0->first_visible_pline == pl && next_pos <= skip_nulls(L, U0->plines[pl].offset, 1)) { move_by_plines(L, pl, -U0->winsize); } else { move_to_top(); } } else { scroll_by_plines(-users[active_window]->winsize); } } void f_up_page(unsigned char c) { int pl; if (active_window == -1) { pl = find_pline(U0, L); while (pl < U0->nplines && skip_nulls(L, U0->plines[pl].offset, 1) < next_pos && U0->plines[pl+1].line == L) pl++; move_by_plines(L, pl, -U0->winsize); } else { scroll_by_plines(-users[active_window]->winsize); } } void f_up_half_page(unsigned char c) { int pl; if (active_window == -1) { pl = find_pline(U0, L); while (plnplines && skip_nulls(L, U0->plines[pl].offset, 1) < next_pos && U0->plines[pl+1].line == L) pl++; move_by_plines(L, pl, -(U0->winsize / 2)); } else { scroll_by_plines(-(users[active_window]->winsize / 2)); } } void f_bottom_or_down_page(unsigned char c) { int pl; if (active_window == -1) { pl = find_pline(U0, L); while (plnplines && skip_nulls(L, U0->plines[pl].offset, 1)plines[pl].line == U0->plines[pl+1].line) pl++; if (U0->first_visible_pline+U0->winsize-1 == pl && next_pos >= U0->plines[pl].offset && plnplines) { move_by_plines(L, pl, U0->winsize); } else { move_to_bottom(); } } else { scroll_by_plines(users[active_window]->winsize); } } void f_down_page(unsigned char c) { int pl; if (active_window == -1) { pl = find_pline(U0, L); while (plnplines && skip_nulls(L, U0->plines[pl].offset, 1)plines[pl].line == U0->plines[pl+1].line) pl++; move_by_plines(L, pl, U0->winsize); } else { scroll_by_plines(users[active_window]->winsize); } } void f_down_half_page(unsigned char c) { int pl; if (active_window == -1) { pl = find_pline(U0, L); while (plnplines && skip_nulls(L, U0->plines[pl].offset, 1)plines[pl].line == U0->plines[pl+1].line) pl++; move_by_plines(L, pl, (U0->winsize) / 2); } else { scroll_by_plines(users[active_window]->winsize / 2); } } void f_redisplay(unsigned char c) { redraw(); } void f_resynch(unsigned char c) { c_resync(); } void f_next_window(unsigned char c) { active_window++; if (active_window >= active_buffers) active_window = -1; } void f_nop(unsigned char c) { } void f_beep(unsigned char c) { beep(); } void f_vi_insert_mode(unsigned char c) { if (!check_write()) return; current_mode = &modes[VI_INS]; vi_prefix = 0; } void f_vi_command_mode(unsigned char c) { current_mode = &modes[VI_CMD]; vi_prefix = 0; } void f_emacs_mode(unsigned char c) { current_mode = &modes[EMACS]; vi_prefix = 0; } void f_quit(unsigned char c) { cleanupexit(0, ""); } void f_vi_escape(unsigned char c) { f_backward(c); f_vi_command_mode(c); } void f_vi_add(unsigned char c) { if (!check_write()) return; f_forward(c); f_vi_insert_mode(c); } void f_vi_open(unsigned char c) { if (!check_write()) return; f_new_line(c); f_vi_insert_mode(c); } void f_vi_Open(unsigned char c) { if (!check_write()) return; if (L->number > 1) L = L->prev; next_pos = skip_nulls(L, 1, 1); set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); f_vi_insert_mode(c); } void f_vi_replace_char(unsigned char c) { if (!check_write()) return; force_next_func = f_insert_in_place; } void f_quote_char(unsigned char c) { if (!check_write()) return; force_next_func = f_self_insert; } static void find_char(unsigned char c) { int n; if (c == 0) return; last_find_char = c; n = next_pos + 1; while (n <= L->length && L->chars[n] != c) n++; if (n <= L->length && L->chars[n] == c) { next_pos = n; set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } else beep(); } static void reverse_find_char(unsigned char c) { int n; if (c == 0) return; last_find_char = c; n = next_pos - 1; while (n >= 1 && L->chars[n] != c) n--; if (n >= 1 && L->chars[n] == c) { next_pos = n; set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } else beep(); } void f_vi_find_char(unsigned char c) { if (!check_write()) return; force_next_func = find_char; last_find_function = find_char; reverse_find_function = reverse_find_char; } void f_vi_reverse_find_char(unsigned char c) { if (!check_write()) return; force_next_func = reverse_find_char; last_find_function = reverse_find_char; reverse_find_function = find_char; } static void till_char(unsigned char c) { int n; if (c == 0) return; last_find_char = c; n = next_pos + 1; while (n <= L->length && L->chars[n] != c) n++; if (n <= L->length && L->chars[n] == c) { n = skip_nulls(L, n-1, -1); if (n >= 1 && L->chars[n] != 0) { next_pos = n; set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } else beep(); } else beep(); } static void reverse_till_char(unsigned char c) { int n; if (c == 0) return; last_find_char = c; n = next_pos - 1; while (n >= 1 && L->chars[n] != c) n--; if (n >= 1 && L->chars[n] == c) { if (n <= L->length) n++; n = skip_nulls(L, n, 1); next_pos = n; set_cursor(U0, L, 0, next_pos); c_set_cursor(L->number, next_pos); } else beep(); } void f_vi_till_char(unsigned char c) { if (!check_write()) return; force_next_func = till_char; last_find_function = till_char; reverse_find_function = reverse_till_char; } void f_vi_reverse_till_char(unsigned char c) { if (!check_write()) return; force_next_func = reverse_till_char; last_find_function = reverse_till_char; reverse_find_function = till_char; } void f_vi_repeat_find(unsigned char c) { if (!check_write()) return; if (last_find_function == NULL) beep(); else (*last_find_function)(last_find_char); } void f_vi_reverse_repeat_find(unsigned char c) { if (!check_write()) return; if (reverse_find_function == NULL) beep(); else (*reverse_find_function)(last_find_char); } static void delete_find_char(unsigned char c) { int n; if (c == 0) return; last_find_char = c; n = next_pos + 1; while (n <= L->length && L->chars[n] != c) n++; if (n <= L->length && L->chars[n] == c) { delete_till(L, n); } else beep(); } static void delete_reverse_find_char(unsigned char c) { int n; if (c == 0) return; last_find_char = c; n = next_pos - 1; while (n >= 1 && L->chars[n] != c) n--; if (n >= 1 && L->chars[n] == c) { delete_till(L, n); } else beep(); } static void delete_till_char(unsigned char c) { int n; if (c == 0) return; last_find_char = c; n = next_pos + 1; while (n <= L->length && L->chars[n] != c) n++; if (n <= L->length && L->chars[n] == c) { n = skip_nulls(L, n-1, -1); if (n >= 1 && L->chars[n] != 0) { delete_till(L, n); } else beep(); } else beep(); } static void delete_reverse_till_char(unsigned char c) { int n; if (c == 0) return; last_find_char = c; n = next_pos - 1; while (n >= 1 && L->chars[n] != c) n--; if (n >= 1 && L->chars[n] == c) { if (n <= L->length) n++; n = skip_nulls(L, n, 1); delete_till(L, n); } else beep(); } void f_vi_delete_find_char(unsigned char c) { if (!check_write()) return; force_next_func = delete_find_char; last_find_function = find_char; reverse_find_function = reverse_find_char; } void f_vi_delete_reverse_find_char(unsigned char c) { if (!check_write()) return; force_next_func = delete_reverse_find_char; last_find_function = reverse_find_char; reverse_find_function = find_char; } void f_vi_delete_till_char(unsigned char c) { if (!check_write()) return; force_next_func = delete_till_char; last_find_function = till_char; reverse_find_function = reverse_till_char; } void f_vi_delete_reverse_till_char(unsigned char c) { if (!check_write()) return; force_next_func = delete_reverse_till_char; last_find_function = reverse_till_char; reverse_find_function = till_char; } void f_vi_flip_case(unsigned char c) { if (!check_write()) return; if (next_pos <= L->length) { c = L->chars[next_pos]; if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= 192 && c <= 254)) { c ^= 0x20; write_char(U0, L, 0, next_pos, c); c_write_char(L->number, next_pos, c); next_pos = skip_nulls(L, ++next_pos, 1); } else if (next_pos <= L->length) { set_cursor(U0, L, 0, ++next_pos); c_set_cursor(L->number, next_pos); } } } void f_do_help(unsigned char c) { do_help(); } void f_set_topic(unsigned char c) { entertopic(); } void f_do_command(unsigned char c) { popup_command(current_mode == &modes[EMACS] ? "M-x " : ":"); } void f_test_menu(unsigned char c) { /* xxx */ test_menu(); } void f_test_entry(unsigned char c) { /* xxx */ test_entry(); } void f_test_selection(unsigned char c) { /* xxx */ test_selection(); } utalk-1.0.1.beta.orig/globals.c0100644000076600007650000000174606376753227015624 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp by Roger Espel Llima globals.c This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ int cols, lines; #ifdef SEVEN_BIT int eight_bit_clean = 0; #else int eight_bit_clean = 1; #endif int beep_on = 1; int connected = 0; int cannotrun = 0; /* if the screen is too small to run */ int wordwrap = 1; int in_main_loop = 0; #ifdef SEVEN_BIT int meta_esc = 1; /* if the meta key gets mapped to ESC-key in emacs mode */ #else int meta_esc = 0; #endif struct user *users[300]; volatile int screen_inited = 0; volatile int mustdie = 0; /* set to 1 by SIGINT handler: shutdown connection and quit */ volatile int winch = 0, newcols, newlines; /* set by SIGWINCH handler */ volatile int mustredisplay = 0; /* set by SIGCONT handler */ utalk-1.0.1.beta.orig/kbd.c0100644000076600007650000001735606376753230014737 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp by Roger Espel Llima kbd.c This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include #include #include #include #include "functions.h" #include "struct.h" #include "rc.h" #include "util.h" #include "globals.h" #include "kbd.h" #include "menu.h" struct mode modes[N_MODES], *current_mode; ftype force_next_func = NULL; /* if set, overrides the binding for the current key and gets unset afterwards; used by vi-replace-char and quote-char to force a self-insert */ int vi_prefix = 0; /* numeric prefix to vi commands */ unsigned char *emacs_bindings[] = { "^i tab", "^m new-line", "^j new-line", "^q quote-char", /* probably not usable b/c of flow control */ "^d delete", "^k delete-end-of-line", "^u delete-line", "^h backspace", "^? backspace", "^w backspace-word", "\\ed delete-word", "\\ef forward-word", "\\eb backward-word", "^a beginning-of-line", "^e end-of-line", "^v down-page", "\\ev up-page", "\\e< top", "\\e> bottom", "^l redisplay", "^r resynch", "^xb next-window", "^xo next-window", "^g next-window", "^xc quit", "^f forward", "^b backward", "^p up", "^n down", "^t set-topic", "\\eH do-help", "\\ex do-command", "\\e[D backward", "\\e[C forward", "\\e[A up", "\\e[B down", NULL }; unsigned char *vi_cmd_bindings[] = { "\\n new-line", "\\r new-line", "h backward", "j down", "k up", "l forward", "i vi-insert-mode", "a vi-add", "I vi-insert-at-beginning-of-line", "A vi-add-at-end-of-line", "R vi-insert-mode", "o vi-open", "O vi-open-above", "r vi-replace-char", "x delete", "X backspace", "dd delete-line", "db backspace-word", "dw delete-word", "dW delete-word", "de delete-end-of-word", "d$ delete-end-of-line", "d0 delete-beginning-of-line", "d\\^ delete-beginning-of-line", "df vi-delete-find-char", "dF vi-delete-reverse-find-char", "dt vi-delete-till-char", "dT vi-delete-reverse-till-char", "D delete-end-of-line", "w forward-word", "W forward-word", "e end-of-word", "b backward-word", "B backward-word", "0 beginning-of-line", "\\^ beginning-of-line", "$ end-of-line", "^d down-half-page", "^f down-page", "^u up-half-page", "^b up-page", "H top-of-screen", "M middle-of-screen", "L bottom-of-screen", "G vi-goto-line", "\e nop", "^l redisplay", "g next-window", "f vi-find-char", "F vi-reverse-find-char", "t vi-till-char", "T vi-reverse-till-char", "; vi-repeat-find", ", vi-reverse-repeat-find", "~ vi-flip-case", "^t set-topic", "^r resynch", ": do-command", "ZZ quit", "H do-help", "[D backward", "[C forward", "[A up", "[B down", NULL }; unsigned char *vi_ins_bindings[] = { "\\e vi-escape", "^? backspace", "^i tab", "^m new-line", "^j new-line", "^u delete-line", "^h backspace", "^w backspace-word", "^l redisplay", "^r resynch", "^v quote-char", NULL }; struct function *find_function(unsigned char *s) { struct function *f = the_functions; for (; f->name[0] != 0; f++) if (strcmp(f->name, (char *)s) == 0) return f; return NULL; } static int hexparse(unsigned char c) { if (c <= '9' && c >= '0') return c - '0'; else if (c <= 'F' && c >= 'A') return c - 'A' + 10; else if (c <= 'f' && c >= 'a') return c - 'a' + 10; return 0; } #define is_hex(c) (((c) <= '9' && (c) >= '0') || ((c) <= 'f' && (c) >= 'a') ||\ ((c) <= 'F' && (c) >= 'A')) char *new_binding(unsigned char *line, int insmode) { unsigned char *r, *nx, sequence[16], *w = sequence; static unsigned char s[200]; struct function *func = NULL; struct mode *mode; struct binding *binding; int add = 0; if (current_mode == &modes[EMACS]) mode = current_mode; else if (insmode) mode = &modes[VI_INS]; else mode = &modes[VI_CMD]; strncpy(s, line, 199); nx = s; r = getword(&nx); while (*r) { if (w>sequence+14) return "Sequence too long"; if (*r == 'M' && r[1] == '-') { add += 128; r += 2; } else if (*r == '^' || (*r == 'C' && r[1] == '-')) { add -= '@'; if (*r == '^') r++; else r += 2; if (*r >= 'a' && *r <= 'z') add -= 32; else if (*r == '?') add = 0x7f - '?'; } else if (*r == '\\' && r[1] != 0) { r++; if (*r == 'e' || *r == 'E') { r++; *(w++) = 0x1b + add; add = 0; } else if (*r == 't' || *r == 'T') { r++; *(w++) = 9 + add; add = 0; } else if (*r == 'n' || *r == 'N') { r++; *(w++) = 10 + add; add = 0; } else if (*r == 'r' || *r == 'R') { r++; *(w++) = 13 + add; add = 0; } else if (*r == 'x') { int c = 0; r++; if (is_hex(*r)) { c = hexparse(*r); r++; if (is_hex(*r)) { c = (c * 16) + hexparse(*r); r++; } } *(w++) = c + add; add = 0; } else { *(w++) = (*(r++) + add); add = 0; } } else { *(w++) = (*(r++)+add); add = 0; } } if (w == sequence) return "Invalid key code"; if (nx == NULL) return "Missing argument"; r = getword(&nx); func = find_function(r); if (func == NULL) return "Unrecognized function name"; if (nx != NULL) return "Too many arguments"; *w = 0; if (w == sequence+1) { mode->funcs[*sequence] = func->func; } else { binding = (struct binding *)mymalloc(sizeof(struct binding)); binding->next = mode->bindings; mode->bindings = binding; binding->func = func->func; strcpy((char *)binding->sequence, (char *)sequence); } return NULL; } static void nb(char *s, int n) { char *t = new_binding(s, n); if (t) { fprintf(stderr, "%s on line '%s'\n", t, s); sleep(1); } } void kbd_defaults(void) { int i; unsigned char **s; for (i=0; i<256; i++) modes[EMACS].funcs[i] = modes[VI_INS].funcs[i] = modes[VI_CMD].funcs[i] = f_beep; for (i=' '; i<='~'; i++) modes[EMACS].funcs[i] = modes[VI_INS].funcs[i] = f_self_insert; for (i=0xa1; i<0xff; i++) modes[EMACS].funcs[i] = modes[VI_INS].funcs[i] = f_self_insert; current_mode = &modes[EMACS]; for (s=emacs_bindings; *s; s++) nb(*s, 0); current_mode = &modes[VI_CMD]; for (s=vi_cmd_bindings; *s; s++) nb(*s, 0); current_mode = &modes[VI_INS]; for (s=vi_ins_bindings; *s; s++) nb(*s, 1); current_mode = &modes[EMACS]; } static int match(unsigned char *s, unsigned char *t) { /* returns the number of matching chars in the 2 strings */ int r = 0; while (*s && *t && *s == *t) { s++; t++; r++; } return r; } void keyboard(unsigned char c) { static unsigned char seq[17]; static int inseq = 0; struct binding *b = NULL; ftype func; int found = 0; if (the_menu != NULL) { menu_keyboard(c); return; } if (force_next_func != NULL) { (*force_next_func)(c); force_next_func = NULL; return; } if (current_mode == &modes[VI_CMD] && inseq == 0 && ((vi_prefix != 0 && c >= '0' && c <= '9') || (c >= '1' && c <= '9'))) { vi_prefix = vi_prefix * 10 + (c - '0'); return; } if (meta_esc && current_mode == &modes[EMACS] && c > 0x7f) { keyboard(0x1b); keyboard(c & 0x7f); return; } if (inseq > 15) inseq = 0; seq[inseq++] = c; seq[inseq] = 0; for (b=current_mode->bindings; b; b=b->next) { if (match(b->sequence, seq) == inseq) { if (strlen(b->sequence) == inseq) { func = b->func; found = 2; break; } found = 1; } } if (found == 1) return; inseq = 0; if (found == 0) func = current_mode->funcs[c]; (*func)(c); vi_prefix = 0; } utalk-1.0.1.beta.orig/menu.c0100644000076600007650000005044406376753233015141 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp Copyright (C) 1995 Roger Espel Llima menu.c Started: 1 Dec 96 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include #include #include #include "globals.h" #include "termcap.h" #include "util.h" #include "screen.h" #include "rc.h" #include "menu.h" #include "comm.h" struct menu *the_menu = NULL; int menu_top, menu_bottom, menu_left, menu_right; /* valid only if the_menu != NULL */ static int last_esc = 0; static char **help_txt = NULL; static char *help_jumpkeys = NULL; static int *help_jumplines = NULL; static char free_keys[] = "acdefghilmnopqrstuvwxyzCDEFGHIJKLMNOPQRSTUVWXZY0123456789"; /* puts the chars on the screen, unless there's a menu */ void m_putscreen(char *s, int n, char attr) { if (the_menu == NULL) putscreen(s, n, attr); } void m_cleareol(void) { if (the_menu == NULL) cleareol(); } void m_gotoxy(int x, int y) { if (the_menu == NULL) gotoxy(x, y); } void m_scrollup(int y1, int y2) { if (the_menu == NULL) scrollup(y1, y2); } void m_scrolldown(int y1, int y2) { if (the_menu == NULL) scrolldown(y1, y2); } void m_backspace(void) { if (the_menu == NULL) backspace(); } static void remove_menu(void) { struct menu *tmp; if (the_menu) { tmp = the_menu; the_menu = NULL; pl_redraw_all(); if (tmp->callback) (*tmp->callback)(tmp); } } static void draw_bottom(int more) { int i; gotoxy(menu_left, menu_bottom); putscreen("`", 1, 0); i = menu_left + 1; if (more) { if (menu_right - i >= 8) { putscreen("-[more]-", 8, 0); i += 8; } else if (menu_right - i >= 3) { putscreen("-m-", 3, 0); i += 3; } } for (; itext + (the_menu->showfrom); for (; y < menu_bottom; ++y, (*s && ++s)) { gotoxy(menu_left + 1, y); tofill = menu_right - menu_left - 1; if (*s) { len = strlen(*s) - the_menu->showfromcol; if (len <= 0) { } else if (len <= tofill) { putscreen(*s + the_menu->showfromcol, len, 0); tofill -= len; } else if (tofill >= 5) { putscreen(*s + the_menu->showfromcol, tofill - 4, 0); putscreen(" ...", 4, 0); tofill = 0; } } for (; tofill; tofill--) putscreen(" ", 1, 0); } } /* draws the entry text in place */ static void draw_entry_text(void) { int y, tofill, len; char *s; y = menu_top + 1; gotoxy(menu_left + 1, y); s = the_menu->entrytxt; tofill = menu_right - menu_left - 1; len = strlen(s); if (len <= tofill) { putscreen(s, len, 0); tofill -= len; } else if (tofill >= 5) { putscreen(s, tofill - 4, 0); putscreen(" ...", 4, 0); tofill = 0; } for (; tofill; tofill--) putscreen(" ", 1, 0); gotoxy(menu_left + 1, y + 1); tofill = menu_right - menu_left - 1; s = the_menu->entry; len = the_menu->len - the_menu->showfromcol; if (len <= 0) { } else if (len <= tofill) { putscreen(s + the_menu->showfromcol, len, 0); tofill -= len; } else { putscreen(s + the_menu->showfromcol, tofill, 0); tofill = 0; } for (; tofill; tofill--) putscreen(" ", 1, 0); gotoxy(menu_left + 1 + the_menu->len - the_menu->showfromcol, y + 1); } /* draws the given menu, clearing any prev. one and setting the_menu to it */ static void draw_menu(struct menu *m) { char **s; if (cannotrun) return; if (the_menu != NULL) { the_menu->preempted = 1; remove_menu(); } the_menu = m; m->preempted = 0; last_esc = 0; switch(the_menu->type) { case MENU_TEXT: case MENU_CHOOSE: m->showfrom = 0; m->showfromcol = 0; s = m->text; m->nlines = 0; m->maxcols = 0; for (; *s; ++s) { int len; m->nlines++; len = strlen(*s); if (len > m->maxcols) m->maxcols = len; } if (m->nlines + 4 > lines) menu_top = 2; else menu_top = 2 + ((lines - (m->nlines + 4)) / 3); menu_bottom = menu_top + m->nlines + 1; m->more = 0; if (menu_bottom >= lines) { menu_bottom = lines - 1; m->more = 1; } if (m->maxcols + 4 > cols) menu_left = 2; else menu_left = 2 + ((cols - (m->maxcols + 4)) / 2); menu_right = menu_left + m->maxcols + 1; if (menu_right >= cols) menu_right = cols - 1; m->wlines = menu_bottom - menu_top - 1; m->wcols = menu_right - menu_left - 1; if (m->type == MENU_CHOOSE) m->text[m->arrowat][3] = '<'; draw_frame(m->more); draw_text_menu(); break; case MENU_ENTRY: m->showfromcol = 0; m->nlines = 2; m->wlines = 2; menu_top = 2 + ((lines - 6) / 3); menu_bottom = menu_top + 3; if (cols < 64) menu_left = 2; else menu_left = 2 + ((cols - 64) / 2); menu_right = menu_left + 60 + 1; if (menu_right >= cols) menu_right = cols - 1; m->wcols = menu_right - menu_left - 1; m->len = strlen(m->entry); m->showfromcol = m->len - m->wcols + 8; if (m->showfromcol < 0) m->showfromcol = 0; draw_frame(0); draw_entry_text(); break; } } void redraw_menu(void) { struct menu *m; m = the_menu; if (m) { the_menu = NULL; draw_menu(m); } } /* dispatch a key in menu mode */ void menu_keyboard(unsigned char c) { struct menu *m = the_menu; char *k; int toline; switch (m->type) { case MENU_TEXT: { int sf = m->showfrom, mr = m->more; if (c == 0x1b) { if (last_esc) remove_menu(); else last_esc = 1; return; } last_esc = 0; if (m->jumpkeys && (k = strchr(m->jumpkeys, c))) { toline = m->jumplines[k - m->jumpkeys]; if (toline >= 0 && toline < m->nlines) { m->showfrom = toline; draw_text_menu(); } } else { switch(c) { case 7: case 8: case 0x7f: case 10: case 13: remove_menu(); return; case 14: case 'B': case 'j': if (m->more) { m->showfrom++; draw_text_menu(); } break; case 'l': case 'C': case 6: if (m->showfromcol + m->wcols < m->maxcols) { m->showfromcol++; draw_text_menu(); } break; case 'h': case 'D': case 2: if (m->showfromcol > 0) { m->showfromcol--; draw_text_menu(); } break; case 9: if (m->showfromcol + m->wcols < m->maxcols) { m->showfromcol += 8; if (m->showfromcol + m->wcols > m->maxcols) m->showfromcol = m->maxcols - m->wcols; draw_text_menu(); } break; case 1: case '0': case '^': if (m->showfromcol > 0) { m->showfromcol = 0; draw_text_menu(); } break; case 5: case '$': if (m->showfromcol + m->wcols < m->maxcols) { m->showfromcol = m->maxcols - m->wcols; draw_text_menu(); } break; case 'k': case 16: case 'A': if (m->showfrom > 0) { m->showfrom--; draw_text_menu(); } break; case ' ': case 'f': case '+': if (m->more) { m->showfrom += m->wlines; if (m->showfrom + m->wlines > m->nlines) m->showfrom = m->nlines - m->wlines; if (m->showfrom != sf) draw_text_menu(); } else if (c == ' ') { remove_menu(); return; } break; case 4: if (m->more) { m->showfrom += m->wlines / 2; if (m->showfrom + m->wlines > m->nlines) m->showfrom = m->nlines - m->wlines; if (m->showfrom != sf) draw_text_menu(); } break; case '-': case 'b': m->showfrom -= m->wlines; if (m->showfrom < 0) m->showfrom = 0; if (m->showfrom != sf) draw_text_menu(); break; case 21: m->showfrom -= m->wlines / 2; if (m->showfrom < 0) m->showfrom = 0; if (m->showfrom != sf) draw_text_menu(); break; } } if (sf != m->showfrom) { m->more = (m->showfrom + m->wlines < m->nlines); if (mr != m->more) draw_bottom(m->more); } } break; case MENU_CHOOSE: { int nsf = m->showfrom, naw = m->arrowat, mr = m->more; char **s; if (c == 0x1b) { if (last_esc) { m->arrowat = -1; remove_menu(); } else last_esc = 1; return; } last_esc = 0; for (s=m->text; *s; s++) { if (c == s[0][1] && s[0][2] == ' ' && s[0][4] == '-') { m->arrowat = s - m->text; remove_menu(); return; } } switch(c) { case 7: case 8: case 0x7f: m->arrowat = -1; case 10: case 13: remove_menu(); return; case 14: case 'B': case 'j': if (m->arrowat+1 < m->nlines) naw++; break; case 'k': case 16: case 'A': if (m->arrowat > 0) naw--; if (naw <= m->arrowmin) nsf--; break; case ' ': case '+': if (m->arrowat+1 < m->nlines) { naw += m->wlines; nsf += m->wlines; } break; case 4: if (m->arrowat+1 < m->nlines) { naw += m->wlines / 2; nsf += m->wlines / 2; } break; case '-': case 'b': if (m->arrowat > 0) { naw -= m->wlines; nsf -= m->wlines; } break; case 21: if (m->arrowat > 0) { naw -= m->wlines / 2; nsf -= m->wlines / 2; } break; } if (naw > nsf + m->wlines - 1) nsf = naw - m->wlines + 1; if (naw < nsf) nsf = naw; if (naw < m->arrowmin) naw = m->arrowmin; else if (naw > m->arrowmax) naw = m->arrowmax; if (nsf > m->nlines - m->wlines) nsf = m->nlines - m->wlines; if (nsf < 0) nsf = 0; if (naw != m->arrowat) { m->text[m->arrowat][3] = '-'; m->text[naw][3] = '<'; } if (nsf != m->showfrom) { m->showfrom = nsf; m->arrowat = naw; draw_text_menu(); m->more = (m->showfrom + m->wlines < m->nlines); if (mr != m->more) draw_bottom(m->more); } else { if (m->arrowat >= m->showfrom && m->arrowat < m->showfrom + m->wlines) { gotoxy(menu_left + 4, menu_top + 1 + m->arrowat - m->showfrom); putscreen("-", 1, 0); } if (naw >= m->showfrom && naw < m->showfrom + m->wlines) { gotoxy(menu_left + 4, menu_top + 1 + naw - m->showfrom); putscreen("<", 1, 0); } m->arrowat = naw; } } break; case MENU_ENTRY: { if (c == 0x1b) { if (last_esc) { m->entry[0] = m->entry[m->promptlen] = 0; remove_menu(); return; } else last_esc = 1; return; } last_esc = 0; switch(c) { case 13: case 10: m->entry[m->len] = 0; remove_menu(); return; case 21: if (m->len > m->promptlen) { m->len = m->promptlen; m->showfromcol = 0; draw_entry_text(); } break; case 8: case 0x7f: if (m->len > m->promptlen) { if (m->len - m->showfromcol <= 4) { m->showfromcol -= m->wcols - 8; if (m->showfromcol < 0) m->showfromcol = 0; m->len--; draw_entry_text(); } else { gotoxy(menu_left + m->len - m->showfromcol, menu_top+2); putscreen(" ", 1, 0); gotoxy(menu_left + m->len - m->showfromcol, menu_top+2); m->len--; } } break; default: if (c >= ' ' && (c < 0x7f || (eight_bit_clean && c > 0xa0))) { if (m->len < 255) { if (m->len - m->showfromcol >= m->wcols - 4) { m->entry[m->len++] = c; m->showfromcol += m->wcols - 8; draw_entry_text(); } else { m->entry[m->len++] = c; gotoxy(menu_left + m->len - m->showfromcol, menu_top+2); putscreen(&c, 1, 0); } } } break; } } break; /* xxx */ } } static void empty_callback(struct menu *m) { free(m); } static void freeing_callback(struct menu *m) { char **s; for (s=m->text; *s; s++) free (*s); free(m->text); free(m); } /* freeit = 1 if the callback needs to free everything in s[] and s itself */ void make_text_menu(char **s, int *jumplines, char *jumpkeys, int freeit) { struct menu *m; m = mymalloc(sizeof(struct menu)); m->type = MENU_TEXT; m->callback = (freeit ? freeing_callback : empty_callback); m->text = s; m->jumplines = jumplines; m->jumpkeys = jumpkeys; draw_menu(m); } static void default_txt_callback(struct menu *m) { if (m->preempted) (*m->txtcallback)(NULL); else { m->entry[m->len] = 0; strcpy(m->buf, m->entry + m->promptlen); (*m->txtcallback)(m->buf); } free(m); } /* prints text on a line, puts prompt and the initial text (from buf) on the next, and lets the user edit it; when done, calls the callback (passes it a NULL if preempted, otherwise buf itself. prompt must be 8 char at most (not checked); buffer must be 256 */ void input_text(char *text, char *prompt, char *buf, text_callback c) { struct menu *m; m = mymalloc(sizeof(struct menu)); m->type = MENU_ENTRY; m->callback = default_txt_callback; m->entrytxt = text; if (strlen(buf) + strlen(prompt) > 256) buf[255 - strlen(prompt)] = 0; m->promptlen = strlen(prompt); m->buf = buf; strcpy(m->entry, prompt); strcat(m->entry, buf); m->txtcallback = c; draw_menu(m); } static void help_error(char *s) { help_txt = mymalloc(3 * sizeof(char *)); help_txt[0] = "Help for utalk not available:"; help_txt[1] = mymalloc(256); sprintf(help_txt[1], "help file %s %s.", HELPFILE, s); help_txt[2] = NULL; make_text_menu(help_txt, NULL, NULL, 0); } void do_help(void) { int hlines, jumps = 0, line = 0; char buf[256]; char *cn, *r; FILE *f; if (help_txt == NULL) { f = fopen(HELPFILE, "r"); if (f == NULL) { help_error("not found"); return; } r = fgets(buf, 254, f); if (r == NULL) { help_error("corrupted"); return; } hlines = atoi(buf); if (hlines < 2 || hlines > 5000) { help_error("corrupted"); return; } help_txt = mymalloc((1 + hlines) * sizeof (char *)); help_jumpkeys = mymalloc(128); help_jumplines = mymalloc(128 * sizeof(int)); while (fgets(buf, 254, f) != NULL && --hlines > 0) { if ((cn = strchr(buf, 10))) *cn = 0; if ((cn = strchr(buf, 13))) *cn = 0; help_txt[line] = mymalloc(4 + strlen(buf)); help_txt[line][0] = ' '; if (buf[0] == '@' && buf[2] == '@') { help_jumpkeys[jumps] = buf[1]; help_jumplines[jumps++] = line; strcpy(&help_txt[line][1], buf+3); } else strcpy(&help_txt[line][1], buf); strcat(help_txt[line], " "); line++; } fclose(f); help_txt[line] = NULL; help_jumpkeys[jumps] = 0; help_jumplines[jumps] = 0; } make_text_menu(help_txt, help_jumplines, help_jumpkeys, 0); } static void command_callback(char *s) { char *r; char **txt; if (s && *s) { r = do_rc_line(s); if (r) { txt = mymalloc(3 * sizeof(char *)); txt[0] = "Error:"; txt[1] = mymalloc(2 + strlen(r)); strcpy(txt[1], r); txt[2] = NULL; make_text_menu(txt, NULL, NULL, 1); } } } void popup_command(char *prompt) { static char buf[256]; buf[0] = 0; input_text("Enter utalk command:", prompt, buf, command_callback); } void topic_callback(char *s) { if (s && *s) { settopic(s); c_send_topic(s); } } void entertopic(void) { static char buf[256]; buf[0] = 0; input_text("New topic:", "", buf, topic_callback); } /* xxx */ void test_sel_callback(struct menu *m) { char **s; static char **txt; int n = m->arrowat; for (s=m->text; *s; s++) free (*s); free(m->text); free(m); if (n >= 0) { txt = mymalloc(3 * sizeof(char *)); txt[0] = mymalloc(40); sprintf(txt[0], "Selection was: %d", n - 1); txt[1] = NULL; make_text_menu(txt, NULL, NULL, 1); } } void test_selection(void) { struct menu *m; static char **txt; int i; txt = mymalloc(51 * sizeof(char *)); for (i=0; i<50; i++) txt[i] = mymalloc(80); strcpy(txt[0], " YaY Menu! "); strcpy(txt[1], " "); for (i=2; i<50; i++) sprintf(txt[i], " %c -- blah blah %d ", free_keys[i-2], i-1); txt[50] = NULL; m = mymalloc(sizeof(struct menu)); m->type = MENU_CHOOSE; m->callback = test_sel_callback; m->text = txt; m->arrowat = 2; m->arrowmin = 2; m->arrowmax = 49; draw_menu(m); } void test_txt_callback(char *s) { static char **txt; txt = mymalloc(3 * sizeof(char *)); txt[0] = mymalloc(20); strcpy(txt[0], "Entered text:"); txt[1] = mymalloc(256); strcpy(txt[1], "\""); strcat(txt[1], s); strcat(txt[1], "\""); txt[2] = NULL; make_text_menu(txt, NULL, NULL, 1); } void test_entry(void) { static char buf[256]; strcpy(buf, "some default"); input_text("Enter something:", "> ", buf, test_txt_callback); } /* xxx */ void test_menu(void) { static char *txt[] = { " GNU GENERAL PUBLIC LICENSE", "", " Version 2, June 1991", "", " Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,", " Cambridge, MA 02139, USA", "", " Everyone is permitted to copy and distribute verbatim copies of this", " license document, but changing it is not allowed.", "", " Preamble", "", "The licenses for most software are designed to take away your freedom to", "share and change it. By contrast, the GNU General Public License is", "intended to guarantee your freedom to share and change free software--to", "make sure the software is free for all its users. This General Public", "License applies to most of the Free Software Foundation's software and", "to any other program whose authors commit to using it. (Some other Free", "Software Foundation software is covered by the GNU Library General", "Public License instead.) You can apply it to your programs, too.", "", "When we speak of free software, we are referring to freedom, not price.", "Our General Public Licenses are designed to make sure that you have the", "freedom to distribute copies of free software (and charge for this", "service if you wish), that you receive source code or can get it if you", "want it, that you can change the software or use pieces of it in new", "free programs; and that you know you can do these things.", "", "To protect your rights, we need to make restrictions that forbid anyone", "to deny you these rights or to ask you to surrender the rights. These", "restrictions translate to certain responsibilities for you if you", "distribute copies of the software, or if you modify it.", "", "For example, if you distribute copies of such a program, whether gratis", "or for a fee, you must give the recipients all the rights that you have.", "You must make sure that they, too, receive or can get the source code.", "And you must show them these terms so they know their rights.", "", "We protect your rights with two steps: (1) copyright the software, and", "(2) offer you this license which gives you legal permission to copy,", "distribute and/or modify the software.", "", "Also, for each author's protection and ours, we want to make certain", "that everyone understands that there is no warranty for this free", "software. If the software is modified by someone else and passed on, we", "want its recipients to know that what they have is not the original, so", "that any problems introduced by others will not reflect on the original", "authors' reputations.", "", "Finally, any free program is threatened constantly by software patents.", "We wish to avoid the danger that redistributors of a free program will", "individually obtain patent licenses, in effect making the program", "proprietary. To prevent this, we have made it clear that any patent must", "be licensed for everyone's free use or not licensed at all.", NULL }; make_text_menu(txt, NULL, NULL, 0); } utalk-1.0.1.beta.orig/rc.c0100644000076600007650000001165606376753235014605 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp by Roger Espel Llima rc.c This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include #include #include #include #include "globals.h" #include "util.h" #include "kbd.h" #include "rc.h" static struct toggle togs[] = { { "beep", &beep_on }, { "eight-bit", &eight_bit_clean }, { "eightbit", &eight_bit_clean }, { "eb", &eight_bit_clean }, { "word-wrap", &wordwrap }, { "wordwrap", &wordwrap }, { "ww", &wordwrap }, { "meta-esc", &meta_esc }, { "metaesc", &meta_esc }, { "me", &meta_esc }, { NULL, NULL } }; static struct alias *alias0 = NULL; char *resolve_alias(char *uh) { struct alias *a; static char uh1[256], *at; int found = 0; for (a=alias0; a; a=a->next) if (a->type == ALIAS_ALL && strcmp(uh, a->from) == 0) return a->to; strncpy(uh1, uh, 255); uh1[254] = 0; if ((at = strchr(uh1, '@')) != NULL) *at = 0; for (a=alias0; a; a=a->next) { if (a->type == ALIAS_BEFORE && strcmp(uh1, a->from) == 0) { found = 1; strncpy(uh1, a->to, 255); uh1[254] = 0; if ((at = strchr(uh, '@')) != NULL) if (strlen(uh1) + strlen(at) < 256) strcat(uh1, at); uh = uh1; break; } } if (!found) { strncpy(uh1, uh, 255); uh1[254] = 0; } at = strchr(uh1, '@'); if (at && at[1]) { at++; for (a=alias0; a; a=a->next) { if (a->type == ALIAS_AFTER && strcmp(at, a->from) == 0) { found = 1; if (strlen(a->to) + (at - uh1) < 255) strcpy(at, a->to); break; } } } if (found) return uh1; else return uh; } char *do_rc_line(unsigned char *b) { unsigned char *r, *nx, *at; struct toggle *t; struct alias *a; int onoff; r = b + strlen(b); while (r > b && (*(r-1) == 13 || *(r-1) == 10)) *--r = 0; r = b; while (*r) if (*(r++) == '#') *(r-1) = 0; nx = b; r = getword(&nx); if (r == NULL) return NULL; if (strcmp((char *)r, "set") == 0 || strcmp((char *)r, "se") == 0 || strcmp((char *)r, "toggle") == 0) { if (nx == NULL) return "Missing argument for set"; r = getword(&nx); if (nx != NULL) nx = getword(&nx); if (*r == 'n' && r[1] == 'o') { r += 2; onoff = 0; if (nx != NULL) return "Too many arguments for set"; } else if (nx == NULL || strcmp(nx, "on") == 0) { onoff = 1; } else if (strcmp(nx, "off") == 0) { onoff = 0; } else return "Bad value"; for (t=&togs[0]; t->var; t++) { if (strcmp(r, t->name) == 0) { *t->var = onoff; return NULL; } } return "No such setting"; } else if (strcmp((char *)r, "alias") == 0) { if (nx == NULL) return "Missing argument"; r = getword(&nx); if (nx == NULL) return "Missing argument"; nx = getword(&nx); a = mymalloc(sizeof (struct alias)); at = strchr(r, '@'); if (at == r) { a->type = ALIAS_AFTER; strncpy(a->from, r+1, 255); a->from[254] = 0; strncpy(a->to, (*nx=='@' ? nx+1 : nx), 255); a->to[254] = 0; } else if (at == r + strlen(r) - 1) { a->type = ALIAS_BEFORE; *at = 0; strncpy(a->from, r, 255); a->from[254] = 0; strncpy(a->to, nx, 255); a->to[254] = 0; if ((at = strchr(a->to, '@')) != NULL) *at = 0; } else { a->type = ALIAS_ALL; strncpy(a->from, r, 255); a->from[254] = 0; strncpy(a->to, nx, 255); a->to[254] = 0; } a->next = alias0; alias0 = a; } else if (strcmp((char *)r, "bind") == 0 || strcmp((char *)r, "bindkey") == 0) { if (nx == NULL) return "Missing argument"; return new_binding(nx, 0); } else if (strcmp((char *)r, "bind!") == 0 || strcmp((char *)r, "bindkey!") == 0) { if (nx == NULL) return "Missing argument"; return new_binding(nx, 1); } else if (strcmp((char *)r, "emacs-mode") == 0 || strcmp((char *)r, "emacs") == 0) { current_mode = &modes[EMACS]; } else if (strcmp((char *)r, "vi-mode") == 0 || strcmp((char *)r, "vi") == 0) { current_mode = &modes[VI_INS]; } else return "Invalid toggle"; return NULL; } void read_rc(void) { unsigned char b[300]; char *warning, *home; int warnings = 0, rcline = 0; FILE *f; kbd_defaults(); if ((home = (unsigned char *)getenv("HOME")) == NULL) return; if (strlen((char *)home)>285) return; strcpy((char *)b, (char *)home); strcat((char *)b, "/.utalkrc"); if ((f = fopen((char *)b, "r")) == NULL) return; while (fgets((char *)b, 299, f) != NULL) { rcline++; if ((warning = do_rc_line(b)) != NULL) { fprintf(stderr, "%s in rc file, line %d\n", warning, rcline); warnings++; } } fclose(f); if (warnings) { printf("\nPress Enter to continue...\n"); fgets(b, 300, stdin); } } utalk-1.0.1.beta.orig/screen.c0100644000076600007650000004526406376762666015472 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp by Roger Espel Llima screen.c This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include #include #include "globals.h" #include "termcap.h" #include "util.h" #include "srdpdata.h" #include "struct.h" #include "functions.h" #include "screen.h" #include "menu.h" static char ascii_trans[] = " !cLxY|$'ca<--R_o+23'mP.,1o>123?AAAAAAACEEEEIIIIDNOOOOO*0UUUUYPBaaaaaaaceeeeiiiidnooooo/0uuuuypy"; int active_buffers = 2; /* all absolute coordinates assume left upper screen = 1,1 */ /* all lines are numbered from 1 */ static char statusbuf[300]; static char *make_left(struct user *u) { static char left[12]; if (u == users[0]) strcpy(left, "[m]"); else if ((u->info.flags&SRDP_FL_RECVD) == 0) strcpy(left, "[n]"); else if ((u->info.flags&SRDP_FL_BROKEN) != 0) strcpy(left, "[b]"); else strcpy(left, "[c]"); if (u == users[0] && active_window == -1) strcat(left, "[*]"); /* else if (u == 0 && active_window == 0) strcat(left, "[R]"); */ else if (u == users[active_window]) strcat(left, "[R]"); else strcat(left, "---"); return left; } void doflags(struct user *u) { /* recalculates the flags for window u, and redisplays them if they have changed */ char *left; int len, x = xcursor, y = ycursor; if (active_window == u->last_active && u->info.flags == u->last_flags) return; u->last_active = active_window; u->last_flags = u->info.flags; left = make_left(u); if (strcmp(u->lefts, left) != 0) { strcpy(u->lefts, left); len = strlen(left); if (len>cols-2) len = cols-2; left[len] = '-'; left[len+1] = 0; m_gotoxy(2, u->ystatus); m_putscreen(left, len+1, 0); if (x >= 0 && y >= 0) m_gotoxy(x, y); } } void setstatus(struct user *u, char *s) { /* sets the status line, or just redisplays it if fed a NULL; always recalculates the flags on the left */ int len, llen, x = xcursor, y = ycursor; char *left; left = make_left(u); llen = strlen(left); if (llen>cols-2) llen = cols-2; strcpy(u->lefts, left); if (s != NULL) strncpy(u->statuslines, s, 290); memset(statusbuf, '-', cols); len = strlen(u->statuslines)+2; if (len > cols-4-llen) len = cols-4-llen; if (((cols-len)>>1) >= llen+3) { statusbuf[((cols-len)>>1)] = statusbuf[((cols+len-2)>>1)] = ' '; memcpy(statusbuf+((cols+2-len)>>1), u->statuslines, len-2); } else { statusbuf[llen+3] = statusbuf[llen+len+2] = ' '; memcpy(statusbuf+llen+4, u->statuslines, len-2); } memcpy(statusbuf+1, left, llen); statusbuf[llen+1] = '-'; m_gotoxy(1, u->ystatus); m_putscreen(statusbuf, cols, 0); if (x >= 0 && y >= 0) m_gotoxy(x, y); } void settopic(unsigned char *s) { setstatus(users[0], s); } static void add_pline(struct user *u, int idx, struct logical_line *lline, int offset, int length) { /* inserts an empty pline with number idx, pointing at lline, offset, length; we assume 1 <= idx <= (nplines+1) */ int c; if (u->nplines >= u->p_arr_length) { u->p_arr_length += PLINE_CHUNK; u->plines = (struct physical_line *)myrealloc(u->plines, (u->p_arr_length+8)*sizeof(struct physical_line)); } for (c = u->nplines; c >= idx; c--) u->plines[c+1] = u->plines[c]; u->nplines++; u->plines[idx].line = lline; u->plines[idx].offset = offset; u->plines[idx].length = length; } static struct logical_line *add_empty_lline(struct user *u, srdp_u32 seq) { /* add a lline at the end of the buffer, and make a pline point to it */ struct logical_line *l; l = (struct logical_line *)mymalloc(sizeof (struct logical_line)); if (u->last_lline != NULL) l->number = 1+u->last_lline->number; else l->number = 1; l->length = 0; l->arr_length = LINE_CHUNK; l->chars = mymalloc(LINE_CHUNK+8); l->seqs = (srdp_u32 *)mymalloc((LINE_CHUNK+8)*sizeof(srdp_u32)); l->len_seq = seq; l->any_deleted = 0; l->next = NULL; l->prev = u->last_lline; if (l->prev != NULL) l->prev->next = l; u->last_lline = l; if (u->first_lline == NULL) u->first_lline = l; add_pline(u, u->nplines+1, l, 1, 0); return l; } void init_screen(void) { int i; struct user *u; users[0] = mymalloc(sizeof (struct user)); users[1] = mymalloc(sizeof (struct user)); users[0]->winsize = (lines-2)>>1; users[1]->winsize = (lines-3)>>1; users[0]->ystatus = 1; users[1]->ystatus = 2+users[0]->winsize; users[0]->yfirst = 2; users[1]->yfirst = 1+users[1]->ystatus; users[0]->last_active = users[1]->last_active = -2; setstatus(users[0], "utalk version " UTALK_VERSION); setstatus(users[1], "not connected"); m_gotoxy(1, 2); flush_term(); for (i = 0; ifirst_lline = u->last_lline = NULL; u->p_arr_length = PLINE_CHUNK; u->plines = (struct physical_line *)mymalloc((u->p_arr_length+8)* sizeof(struct physical_line)); u->nplines = 0; add_empty_lline(u, 0); u->first_visible_pline = 1; } current_lline = users[0]->first_lline; } static void printchar(unsigned char c) { if (c == 0) { } else if (c == 0xff) { c = ' '; m_putscreen((char *)&c, 1, 0); } else if (c<' ') { setinv(); c += '@'; m_putscreen((char *)&c, 1, ATTR_INVERSE); normal(); } else if (c >= 0x7f && c<0xa0) { c += '@'; c &= 0x7f; setinv(); m_putscreen((char *)&c, 1, ATTR_INVERSE); normal(); } else if (c >= 0xa0) { if (!eight_bit_clean) c = ascii_trans[c-0xa0]; m_putscreen((char *)&c, 1, 0); } else m_putscreen((char *)&c, 1, 0); } static void draw_line_from_plines(struct user *u, int pl) { /* pl is a pline number; assumes pl is a visible line, but it can be pl>u->nplines */ struct logical_line *l; int idx, max; m_gotoxy(1, u->yfirst+pl-u->first_visible_pline); if (pl>u->nplines) m_cleareol(); else { l = u->plines[pl].line; idx = u->plines[pl].offset; max = l->length; while (idx <= max && xcursor>0 && xcursor <= cols) printchar((unsigned char)l->chars[idx++]); if (xcursor >= 0) m_cleareol(); } } static void draw_end_of_line_from_plines(struct user *u, int pl, int idx, int x0) { struct logical_line *l; int max; m_gotoxy(x0, u->yfirst+pl-u->first_visible_pline); l = u->plines[pl].line; max = l->length; while (idx <= max && xcursor>0 && xcursor <= cols) printchar((unsigned char)l->chars[idx++]); if (xcursor >= 0) m_cleareol(); } static void redraw_from_plines(struct user *u) { int pl; for (pl = u->first_visible_pline; plfirst_visible_pline+u->winsize; ++pl) draw_line_from_plines(u, pl); } void scroll_to_pline(struct user *u, int pl) { #ifdef DEBUG fprintf(stderr, "scroll to pline %u %u\r\n", u, pl); /* DEBUG */ #endif /* DEBUG */ if (pl == u->first_visible_pline) { /* nothing! */ } else if (pl == u->first_visible_pline+1) { if (pl <= u->nplines) { u->first_visible_pline++; m_scrollup(u->yfirst, u->yfirst+u->winsize-1); draw_line_from_plines(u, u->first_visible_pline+u->winsize-1); } } else if (pl == u->first_visible_pline-1) { if (pl >= 1) { u->first_visible_pline--; m_scrolldown(u->yfirst, u->yfirst+u->winsize-1); draw_line_from_plines(u, u->first_visible_pline); } } else if (pl >= 1 && pl <= u->nplines) { u->first_visible_pline = pl; redraw_from_plines(u); } } int find_pline(struct user *u, struct logical_line *l) { /* finds the lline's first pline */ int p1 = 1, p2 = u->nplines, p3; while (u->plines[p1].line->number < l->number && u->plines[p2].line->number > l->number) { p3 = (p1+p2)>>1; if (u->plines[p3].line->number < l->number) p1 = p3; else p2 = p3; } if (u->plines[p2].line->number == l->number) p1 = p2; while (p1>1 && u->plines[p1-1].line->number == l->number) p1--; return p1; } struct logical_line *find_lline(struct user *u, int n, srdp_u32 seq) { /* finds the lline, creating it if necessary but not displaying anything */ int p1 = 1, p2 = u->nplines, p3; struct logical_line *l; if (n <= u->last_lline->number) { while (u->plines[p1].line->number < n && u->plines[p2].line->number > n) { p3 = (p1+p2)>>1; if (u->plines[p3].line->number < n) p1 = p3; else p2 = p3; } if (u->plines[p2].line->number == n) p1 = p2; return u->plines[p1].line; } else { while (n>u->last_lline->number) l = add_empty_lline(u, seq); return l; } } static void extend_lline(struct user *u, struct logical_line *l, srdp_u32 seq, int len) { /* extends the lline to length len, if it was shorter, updating the plines, and the screen if visible, and updating u->first_visible_pline if it changed; ignores it if seqlen_seq */ int pl, toadd, addhere, pointing, c; if (seq>l->len_seq) l->len_seq = seq; else if (seqlen_seq) return; if (l->length >= len) return; pointing = 1+l->length; toadd = len-l->length; if (len>l->arr_length) { l->arr_length += LINE_CHUNK; l->chars = (unsigned char *)myrealloc(l->chars, l->arr_length+8); l->seqs = (srdp_u32 *)myrealloc(l->seqs, (l->arr_length+8)*sizeof(srdp_u32)); } for (c = 0; cchars[c+1+l->length] = 0xff; l->seqs[c+1+l->length] = 0; } l->length = len; l->len_seq = seq; pl = find_pline(u, l); while (plnplines && u->plines[pl+1].line->number == l->number) pl++; /* pl = last pline for l */ addhere = cols-u->plines[pl].length; if (addhere>toadd) addhere = toadd; if (addhere>0) { toadd -= addhere; u->plines[pl].length += addhere; pointing += addhere; } while (toadd>0) { addhere = (toadd <= cols ? toadd : cols); add_pline(u, pl+1, l, pointing, addhere); if (pl >= u->first_visible_pline && plfirst_visible_pline+u->winsize) { /* extending a line the last pline of which is visible */ if (u->nplines == u->first_visible_pline+u->winsize) { /* and we're at the end of the buffer (special case) */ u->first_visible_pline++; m_scrollup(u->yfirst, pl+1-u->first_visible_pline+u->yfirst); } else if (pl != u->first_visible_pline+u->winsize-1) m_scrolldown(u->yfirst+pl+1-u->first_visible_pline, u->yfirst+u->winsize-1); } else if (plfirst_visible_pline) u->first_visible_pline++; pl++; pointing += addhere; toadd -= addhere; } } static void remove_pline(struct user *u, int pl) { /* removes pline number pl */ int c; for (c = pl; cnplines; c++) u->plines[c] = u->plines[c+1]; u->nplines--; } int get_plength(struct user *u, int *pl, int pos) { /* returns the position in the pline pl or one of the following (if they refer to the same lline), not counting nulls, at which the character 'pos' in the lline is, i.e a cursor position (left = 1); updates pl to point to that line; can be called with pos = 0; if called with pos>length-of-the-right-lline, returns the position after, which can be cols+1 */ int r = 1, i = 0; while (*plnplines && u->plines[*pl].line == u->plines[(*pl)+1].line && u->plines[(*pl)+1].offset <= pos) (*pl)++; if (!u->plines[*pl].line->any_deleted) { r = pos - u->plines[*pl].offset + 1; if (r > u->plines[*pl].line->length - u->plines[*pl].offset + 2) r = u->plines[*pl].line->length - u->plines[*pl].offset + 2; if (r < 1) r = 1; } else { while (u->plines[*pl].offset+i < pos && (u->plines[*pl].offset+i <= u->plines[*pl].line->length)) { if (u->plines[*pl].line->chars[u->plines[*pl].offset+i] != 0) r++; ++i; } } return r; } void shorten_lline(struct user *u, struct logical_line *l, srdp_u32 seq, int len) { /* shortens the lline to length len, if it was shorter, and lengthens if it was longer, updating the plines, and the screen if visible, and updating u->first_visible_pline if it changed ; ignores it if seqlen_seq */ int pl, pos, len0; #ifdef DEBUG fprintf(stderr, "shorten_lline %d %d %d\r\n", u, l->number, len); /* DEBUG */ #endif /* DEBUG */ if (seq>l->len_seq) l->len_seq = seq; else if (seqlen_seq) return; extend_lline(u, l, seq, len); len0 = l->length; l->length = len; l->len_seq = seq; pl = find_pline(u, l); while (plnplines && u->plines[pl+1].line->number == l->number) pl++; while (u->plines[pl].offset>len && u->plines[pl].offset>1) { remove_pline(u, pl); if (pl >= u->first_visible_pline && plfirst_visible_pline+u->winsize) { m_scrollup(u->yfirst+pl-u->first_visible_pline, u->yfirst+u->winsize-1); if (u->first_visible_pline>u->nplines) { u->first_visible_pline--; draw_line_from_plines(u, u->first_visible_pline); } } else if (plfirst_visible_pline) u->first_visible_pline--; pl--; } if (len>0) pos = get_plength(u, &pl, len); else pos = 0; u->plines[pl].length = pos; if (pos= u->first_visible_pline && plfirst_visible_pline+u->winsize) if (len == len0-1 && posyfirst+pl-u->first_visible_pline); m_backspace(); } else { m_gotoxy(pos+1, u->yfirst+pl-u->first_visible_pline); m_cleareol(); } } void set_cursor(struct user *u, struct logical_line *l, srdp_u32 seq, int pos) { /* puts the cursor at the given position, unless the line is shorter with a higher seq number; will scroll if necessary */ int pl; #ifdef DEBUG fprintf(stderr, "set_cursor %d %d %d\r\n", u, l->number, pos); /* DEBUG */ #endif /* DEBUG */ if (seqlen_seq && poslength) return; extend_lline(u, l, seq, pos-1); pl = find_pline(u, l); if (pos <= l->length) pos = get_plength(u, &pl, pos); else if (pos != 1) { pos = get_plength(u, &pl, pos-1); if (++pos>cols) pos = cols; } if (plfirst_visible_pline) scroll_to_pline(u, pl); else if (pl >= u->first_visible_pline+u->winsize) scroll_to_pline(u, pl-u->winsize+1); m_gotoxy(pos, u->yfirst+pl-u->first_visible_pline); } void write_char(struct user *u, struct logical_line *l, srdp_u32 seq, int pos, unsigned char c) { /* puts the character at the given position in the llines, will fix the plines if it's a null, and prints it if it's visible */ int pl, ppos, cpl; char oc; #ifdef DEBUG if (c >= ' ') /* DEBUG */ fprintf(stderr, "write_char %d %d %d '%c'\r\n", u, l->number, pos, c); /* DEBUG */ else /* DEBUG */ fprintf(stderr, "write_char %d %d %d %.2x\r\n", u, l->number, pos, c); /* DEBUG */ #endif /* DEBUG */ if (seqlen_seq && pos>l->length) return; extend_lline(u, l, seq, pos); pl = find_pline(u, l); if (l->seqs[pos]>seq) return; ppos = get_plength(u, &pl, pos); oc = l->chars[pos]; l->chars[pos] = c; l->seqs[pos] = seq; if (c == 0) { l->any_deleted = 1; if (oc == 0) return; cpl = pl; u->plines[cpl].length--; if (pl >= u->first_visible_pline && plfirst_visible_pline+u->winsize) draw_end_of_line_from_plines(u, pl, pos, ppos); while (cplnplines && u->plines[cpl+1].line == l && u->plines[cpl+1].length>0) { u->plines[cpl].length++; u->plines[cpl+1].length--; while (u->plines[cpl+1].offset <= l->length && l->chars[u->plines[cpl+1].offset++] == 0); cpl++; if (cpl >= u->first_visible_pline && cplfirst_visible_pline+u->winsize) draw_line_from_plines(u, cpl); } if (cpl <= u->nplines && u->plines[cpl].line == l && u->plines[cpl].length == 0) { /* need to destroy cpl */ remove_pline(u, cpl); if (cpl >= u->first_visible_pline && cplfirst_visible_pline+u->winsize) { m_scrollup(u->yfirst+cpl-u->first_visible_pline, u->yfirst+u->winsize-1); if (u->first_visible_pline>u->nplines) { u->first_visible_pline--; draw_line_from_plines(u, u->first_visible_pline); } } else if (cplfirst_visible_pline) u->first_visible_pline--; } if (pl >= u->first_visible_pline && plfirst_visible_pline+u->winsize) m_gotoxy(ppos, u->yfirst+pl-u->first_visible_pline); } else if (pl >= u->first_visible_pline && plfirst_visible_pline+u->winsize) { m_gotoxy(ppos, u->yfirst+pl-u->first_visible_pline); printchar(c); } else if (u == users[0] || u != users[active_window == -1 ? 0 : active_window]) { cpl = pl+1-u->winsize; if (cpl<1) cpl = 1; scroll_to_pline(u, cpl); m_gotoxy(ppos, u->yfirst+pl-u->first_visible_pline); printchar(c); } } void recalc_all(void) { struct logical_line **oldcll, *l; struct user *u; int *oldpos, lastpl, idx, pl, keeppl = 0, i; oldpos = (int *)mymalloc(active_buffers*sizeof(int)); oldcll = (struct logical_line **)mymalloc(active_buffers*sizeof(l)); for (i = 0; ifirst_visible_pline+u->winsize-1>u->nplines) lastpl = u->nplines; else lastpl = u->first_visible_pline+u->winsize-1; oldcll[i] = u->plines[lastpl].line; if (lastpl == u->nplines || u->plines[lastpl+1].line != oldcll[i]) oldpos[i] = oldcll[i]->length; else oldpos[i] = u->plines[lastpl+1].offset-1; } users[0]->winsize = (lines-2)>>1; users[1]->winsize = (lines-3)>>1; users[0]->ystatus = 1; users[1]->ystatus = 2+users[0]->winsize; users[0]->yfirst = 2; users[1]->yfirst = 1+users[1]->ystatus; setstatus(users[0], NULL); setstatus(users[1], NULL); for (i = 0; inplines = 0; l = u->first_lline; while (l != NULL) { idx = 1; add_pline(u, u->nplines+1, l, 1, 0); pl = u->nplines; while (idx <= l->length) { if (u->plines[pl].length >= cols) { add_pline(u, u->nplines+1, l, idx, 0); pl = u->nplines; } if (l == oldcll[i] && idx == oldpos[i]) keeppl = pl; if (l->chars[idx++] != 0) u->plines[pl].length++; } l = l->next; } if (keeppl >= u->winsize) u->first_visible_pline = 1+keeppl-u->winsize; else u->first_visible_pline = 1; redraw_from_plines(u); } free(oldcll); free(oldpos); } void pl_redraw_screen_zone(int y1, int y2) { struct user *u; int i, pl; for (i=0; iystatus >= y1 && u->ystatus <= y2) setstatus(u, NULL); if (u->yfirst <= y2 && u->yfirst + u->winsize - 1 >= y1) { pl = u->first_visible_pline; if (u->yfirst <= y1) pl = u->first_visible_pline + y1 - u->yfirst; while (pl < u->first_visible_pline + u->winsize && pl <= u->first_visible_pline + y2 - u->yfirst) draw_line_from_plines(u, pl++); } } } void pl_redraw_all(void) { int i; for (i=0; i This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include #include #include #include "globals.h" #include "termio.h" #include "termcap.h" #include "util.h" #include "signal.h" #ifndef USE_SGTTY #include #endif #if !defined(SA_INTERRUPT) && !defined(SA_RESTART) #define NO_SIGACTION #endif #ifdef USE_SIGVEC #define NO_SIGACTION #endif static void putsignal(int n, v_i s) { #ifdef USE_SIGVEC struct sigvec sig; sig.sv_handler = s; sig.sv_mask = 0; sig.sv_flags = 0; #ifdef SV_INTERRUPT sig.sv_flags |= SV_INTERRUPT; #endif sigvec(n, &sig, NULL); #else #ifndef NO_SIGACTION struct sigaction sig; sig.sa_flags = 0; sigemptyset(&sig.sa_mask); sig.sa_handler = s; #ifdef SA_INTERRUPT sig.sa_flags |= SA_INTERRUPT; #endif sigaction(n, &sig, NULL); #else signal(n, s); #endif #endif } static void interrupted(int n) { /* SIGINT + SIGHUP handler */ static int interrupts = 0; if (!in_main_loop) cleanupexit(1, "^C"); if (++interrupts >= 3) cleanupexit(1, "interrupted"); mustdie = 1; putsignal(SIGINT, interrupted); putsignal(SIGHUP, interrupted); } static void sigpipe(int n) { cleanupexit(1, "broken pipe (received SIGPIPE)"); } static void sigquit(int n) { cleanupexit(1, "aborting..."); } static void sigcont(int n) { allsignals(); if (in_main_loop) mustredisplay = 1; else { redraw(); flush_term(); } settty(SET_RAW, 0); } static void suspend(int n) { int x = xcursor, y = ycursor; normal(); gotoxy(0, lines-1); flush_term(); cleareol(); xcursor = x; ycursor = y; settty(SET_COOKED, 0); putsignal(SIGTSTP, SIG_DFL); putsignal(SIGCONT, sigcont); kill(getpid(), SIGTSTP); } static void sigwinch(int n) { #ifdef TIOCGWINSZ struct winsize wsz; if (ioctl(TTYREAD, TIOCGWINSZ, &wsz) >= 0 && wsz.ws_row != 0 && wsz.ws_col != 0) { winch = 1; newcols = wsz.ws_col; newlines = wsz.ws_row; } putsignal(SIGWINCH, sigwinch); #endif } void allsignals(void) { putsignal(SIGHUP, interrupted); putsignal(SIGINT, interrupted); putsignal(SIGQUIT, sigquit); putsignal(SIGPIPE, sigpipe); putsignal(SIGTSTP, suspend); putsignal(SIGCONT, sigcont); #ifdef TIOCGWINSZ putsignal(SIGWINCH, sigwinch); #endif } utalk-1.0.1.beta.orig/srdp.c0100644000076600007650000007102306376753242015141 0ustar garabikatlas/* SRDP, Semi-Reliable Datagram Protocol Copyright (C) 1995 Roger Espel Llima srdp.c Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include #include #include #include #include #include #include #include "srdpdata.h" #include "srdp.h" static struct srdp_infolist *srdp_allinfos = NULL; static srdp_u8 packbuf[MAXPACKETLEN], readbuf[MAXPACKETLEN], *reading; static int sofar, toread; /* internal functions */ static int cansend(struct srdp_info *info) { if (info->remoteport == 0) return 0; if (info->remote.s_addr == htonl(INADDR_ANY)) return 0; return 1; } static void initchunk (struct srdp_chunk *c) { c->u.noseq.version = SRDP_VERSION; c->u.noseq.revision = SRDP_REVISION; } static void sendpacket(struct srdp_info *info, unsigned char *packet, int size) { struct sockaddr_in to; #ifdef DEBUG if (((rand()>>1)%7)<1) /* DEBUG */ fprintf(stderr, "xxx deciding not to send packet\n"); /* DEBUG */ else /* DEBUG */ #endif /* DEBUG */ if (size) { memset((char *)&to, 0, sizeof(struct sockaddr)); to.sin_port = htons(info->remoteport); to.sin_family = AF_INET; to.sin_addr = info->remote; sendto(info->sockfd, (char *)packet, size, 0, (struct sockaddr *)&to, sizeof(struct sockaddr)); } } static void sendit(struct srdp_info *info) { sendpacket(info, packbuf, sofar); sofar = 0; } static void closeandforget(struct srdp_info *info) { struct srdp_chunk_list *lists, *l0; struct srdp_infolist *ilist, *prv; close(info->sockfd); info->flags = SRDP_FL_CLOSED; free(info->recvdarray); lists = info->recvlist_first; while (lists != NULL) { l0 = lists; lists = lists->next; free(l0); } lists = info->sentlist_first; while (lists != NULL) { l0 = lists; lists = lists->next; free(l0); } ilist = srdp_allinfos; prv = NULL; while (ilist != NULL) { if (ilist->theinfo == info) { if (prv == NULL) srdp_allinfos = ilist->next; else prv->next = ilist->next; free(ilist); break; } else { prv = ilist; ilist = ilist->next; } } } static int missing(struct srdp_info *info, srdp_u32 last) { /* tells if there are any missing packets on *info, where last is the last one that should be there */ int idx; srdp_u32 refto, msk; refto = info->rarr_seqbase; idx = info->rarr_start; while (refto+31 <= last) { if (info->recvdarray[idx] != 0xffffffff) return(1); idx = (idx+1)&SRDP_RARRMASK; refto += 32; } if (refto>last) return 0; msk = (1<<(last+1-refto))-1; if ((info->recvdarray[idx]&msk) != msk) return(1); else return(0); } static void make_missing(struct srdp_info *info, srdp_u32 last) { /* makes a MISSLST packet on packbuf after sofar, updates sofar; considers "last" to be the last that should have been received, and assumes rarr goes all the way to last */ srdp_u8 *where = packbuf+sofar, *wlen = packbuf+sofar+4, howmany; int idx, adding = 0, sofar0 = sofar; srdp_u32 refto, msk, nref; *where = SRDP_VERSION; *(where+1) = SRDP_REVISION; *(where+2) = 0; *(where+3) = SRDP_MISSLST; sofar += 8; where += 8; idx = (info->rarr_start+((last-info->rarr_seqbase)>>5))&SRDP_RARRMASK; msk = 1<<((last-info->rarr_seqbase)&0x1f); refto = last; do { if (adding) { if ((info->recvdarray[idx]&msk) == 0 && howmany<255) howmany++; else { where += 4; *(where++) = howmany-1; sofar += 5; adding = 0; } } if (!adding && (info->recvdarray[idx]&msk) == 0) { adding = 1; nref = htonl(refto); memcpy((char *)where, (char *)&nref, 4); howmany = 1; } if (msk>1) msk = msk>>1; else { msk = (srdp_u32)0x80000000; idx = (idx-1)&SRDP_RARRMASK; } refto--; } while (refto >= info->rarr_seqbase && sofarmax_packet_len-16); idx = htonl(sofar-sofar0); memcpy((char *)wlen, (char *)&idx, 4); } static void srdp_sendcurr(struct srdp_info *info, int force) { /* sends a SRDP_CURRENT and updates everything; will also send a SRDP_MISSLIST if the list is not empty */ struct srdp_chunk *c; struct timeval now; c = (struct srdp_chunk *)packbuf; initchunk(c); c->u.noseq.type = SRDP_CURRENT; c->u.noseq.len = htonl(SRDP_HEADER_USEQ_LEN+4); *(int *)&(c->u.noseq.data) = htonl(info->current_seq); sofar = SRDP_HEADER_USEQ_LEN+4; gettimeofday(&now, NULL); info->lastsentsomething = now; info->lastsentcurrent = now; info->current_sent = 1; if (force || now.tv_sec>info->lastsentlist.tv_sec+info->miss_time || (now.tv_sec == info->lastsentlist.tv_sec+info->miss_time && now.tv_usec >= info->lastsentlist.tv_usec)) { if (missing(info, info->current_seq)) { make_missing(info, info->current_seq); info->lastsentlist = now; } } sendit(info); } static void rollrarr(struct srdp_info *info, srdp_u32 last) { /* rolls the array around so that the sequence number "last" falls within it, forgetting old data */ while (info->rarr_seqbase+(SRDP_RARRSIZE<<5) <= last) { info->rarr_seqbase += 32; info->recvdarray[info->rarr_start] = 0; info->rarr_start = (info->rarr_start+1)&SRDP_RARRMASK; } } static void rarrforget(struct srdp_info *info, srdp_u32 oldest) { /* marks as received anything older than oldest */ int idx; srdp_u32 refto, msk; refto = info->rarr_seqbase; idx = info->rarr_start; while (refto+31recvdarray[idx] = (srdp_u32)0xffffffff; idx = (idx+1)&SRDP_RARRMASK; refto += 32; } if (refto >= oldest) return; msk = (1<<(oldest-refto))-1; info->recvdarray[idx]|= msk; } static void resend_all(struct srdp_info *info) { struct timeval now; struct srdp_chunk_list *cl; gettimeofday(&now, NULL); cl = info->sentlist_first; while (cl != NULL) { if (info->max_packet_len < sofar + 16 + ntohl(cl->chunk->u.sequenced.len)) sendit(info); memcpy((char *)packbuf+sofar, (char *)cl->chunk, ntohl(cl->chunk->u.sequenced.len)); sofar += ntohl(cl->chunk->u.sequenced.len); info->lastsentsomething = now; cl = cl->next; } } static void srdp_stufftoread(struct srdp_info *info) { /* reads from the fd and updates everything */ struct sockaddr_in from; struct timeval now; srdp_u32 chunksize, junk, junk2; size_t fromlen = sizeof(struct sockaddr_in); int readsize; readsize = recvfrom(info->sockfd, (char *)readbuf, MAXPACKETLEN, 0, (struct sockaddr *)&from, &fromlen); if (readsize < SRDP_HEADER_USEQ_LEN) return; sofar = 0; if (info->remoteport != 0 && from.sin_port != htons(info->remoteport)) return; if (info->remote.s_addr != htonl(INADDR_ANY) && info->remote.s_addr != from.sin_addr.s_addr) return; if (info->remoteport == 0 || info->remote.s_addr == htonl(INADDR_ANY)) { info->remoteport = ntohs(from.sin_port); info->remote.s_addr = from.sin_addr.s_addr; resend_all(info); } gettimeofday(&now, NULL); info->lastheard = now; info->current_sent = 0; info->flags&=~SRDP_FL_BROKEN; info->flags|= SRDP_FL_RECVD; reading = readbuf; toread = readsize; while (toread) { if (toread toread) break; switch(reading[3]) { case SRDP_PING: if (info->max_packet_len < sofar + chunksize) sendit(info); memcpy((char *)packbuf+sofar, (char *)reading, chunksize); packbuf[sofar+3] = SRDP_PINGREP; sofar += chunksize; reading += chunksize; toread -= chunksize; info->lastsentsomething = now; break; case SRDP_ALIVE: reading += chunksize; toread -= chunksize; break; case SRDP_CURRENT: { struct srdp_chunk_list *cl; if (chunksize >= 12) { memcpy((char *)&junk, (char *)reading+8, 4); junk2 = ntohl(junk); if (junk2next_seq-1) { cl = info->sentlist_last; while (cl != NULL && ((cl->chunk->u.noseq.type&1) != 0 || (ntohl(cl->chunk->u.sequenced.seq)>junk2))) cl = cl->prev; if (cl == NULL) { /* maybe we should send an oldest here */ } else while (cl->next != NULL) { if (info->max_packet_len-sofar < 16 + ntohl(cl->next->chunk->u.sequenced.len)) sendit(info); memcpy((char *)packbuf+sofar, (char *)cl->next->chunk, ntohl(cl->next->chunk->u.sequenced.len)); sofar += ntohl(cl->next->chunk->u.sequenced.len); info->lastsentsomething = now; cl = cl->next; } } } reading += chunksize; toread -= chunksize; break; } case SRDP_OLDEST: if (chunksize >= 12) { memcpy((char *)&junk, (char *)reading+8, 4); junk2 = ntohl(junk); rarrforget(info, junk2); } reading += chunksize; toread -= chunksize; break; case SRDP_MISSLST: { srdp_u32 todo, seq, junk; unsigned int extra, send_oldest = 0; struct srdp_chunk_list *cl; todo = chunksize-8; toread -= 8; reading += 8; cl = info->sentlist_last; while (todo >= 5 && cl != NULL) { memcpy((char *)&junk, (char *)reading, 4); extra = 1+(unsigned int)reading[4]; seq = ntohl(junk); for (; extra != 0; extra--) { while (cl != NULL && ((cl->chunk->u.noseq.type&1) != 0 || ntohl(cl->chunk->u.sequenced.seq) != seq)) cl = cl->prev; if (cl != NULL) { if (info->max_packet_len-sofar < 16 + ntohl(cl->chunk->u.sequenced.len)) sendit(info); memcpy((char *)packbuf+sofar, (char *)cl->chunk, ntohl(cl->chunk->u.sequenced.len)); sofar += ntohl(cl->chunk->u.sequenced.len); info->lastsentsomething = now; cl = cl->prev; } else { send_oldest++; break; } seq--; } todo -= 5; toread -= 5; reading += 5; } if (todo || send_oldest) { cl = info->sentlist_first; while (cl != NULL && (cl->chunk->u.noseq.type&1) != 0) if (cl) cl = cl->next; if (cl) { if (info->max_packet_len-sofar<32) sendit(info); packbuf[sofar++] = SRDP_VERSION; packbuf[sofar++] = SRDP_REVISION; packbuf[sofar++] = 0; packbuf[sofar++] = SRDP_OLDEST; junk = htonl(SRDP_HEADER_USEQ_LEN+4); memcpy((char *)packbuf+sofar, (char *)&junk, 4); junk = htonl(cl->chunk->u.sequenced.seq); memcpy((char *)packbuf+sofar+4, (char *)&junk, 4); sofar += 8; } } reading += todo; toread -= todo; } break; case SRDP_CLOSE: packbuf[0] = SRDP_VERSION; packbuf[1] = SRDP_REVISION; packbuf[2] = 0; packbuf[3] = SRDP_DROP; junk = htonl(8); memcpy((char *)packbuf+4, (char *)&junk, 4); sendpacket(info, packbuf, 8); sendpacket(info, packbuf, 8); sendpacket(info, packbuf, 8); /* drops (no pun intended) into the next: */ case SRDP_DROP: closeandforget(info); return; default: { struct srdp_chunk *c; struct srdp_chunk_list *cl, *ncl; int skipit = 0; c = (struct srdp_chunk *)malloc(chunksize); ncl = (struct srdp_chunk_list *)malloc(sizeof (struct srdp_chunk_list)); if (c == NULL || ncl == NULL) { reading += chunksize; toread -= chunksize; break; } memcpy((char *)c, (char *)reading, chunksize); reading += chunksize; toread -= chunksize; if ((c->u.noseq.type&1) == 0) { memcpy((char *)&junk, (char *)&(c->u.sequenced.seq), 4); junk2 = ntohl(junk); if (junk2>info->current_seq) info->current_seq = junk2; rollrarr(info, junk2); if (junk2 >= info->rarr_seqbase) { skipit = info->recvdarray[(((junk2-info->rarr_seqbase)>>5)+ info->rarr_start)&SRDP_RARRMASK] & (1<<((junk2-info->rarr_seqbase)&31)); info->recvdarray[(((junk2-info->rarr_seqbase)>>5)+ info->rarr_start)&SRDP_RARRMASK] |= 1<<((junk2-info->rarr_seqbase)&31); } } if (!skipit) { while (info->recvlist_size + CHUNKLIST_EXTRA + ntohl(c->u.sequenced.len) > info->max_rcv_lst_size) { cl = info->recvlist_first; if (cl->next != NULL) cl->next->prev = NULL; info->recvlist_first = cl->next; info->recvlist_size -= cl->lsize; if (info->recvlist_last == cl) info->recvlist_last = NULL; free(cl->chunk); free(cl); } info->flags|= SRDP_FL_READY; ncl->prev = info->recvlist_last; ncl->next = NULL; ncl->lsize = CHUNKLIST_EXTRA+ntohl(c->u.sequenced.len); info->recvlist_size += CHUNKLIST_EXTRA+ntohl(c->u.sequenced.len); ncl->chunk = c; if (ncl->prev) ncl->prev->next = info->recvlist_last = ncl; else info->recvlist_last = info->recvlist_first = ncl; } if (!toread) { if (now.tv_sec>info->lastsentlist.tv_sec+info->miss_time || (now.tv_sec == info->lastsentlist.tv_sec+info->miss_time && now.tv_usec >= info->lastsentlist.tv_usec)) { if (missing(info, info->current_seq)) { if (info->max_packet_len-sofar-16<256) sendit(info); make_missing(info, info->current_seq); info->lastsentlist = now; } } } } } } sendit(info); } static void srdp_sendalive(struct srdp_info *info) { /* sends a SRDP_ALIVE and updates everything */ struct srdp_chunk *c = (struct srdp_chunk *)packbuf; struct timeval now; initchunk(c); c->u.noseq.type = SRDP_ALIVE; c->u.noseq.len = htonl(SRDP_HEADER_USEQ_LEN); sendpacket(info, packbuf, SRDP_HEADER_USEQ_LEN); gettimeofday(&now, NULL); info->lastsentsomething = now; } /* interface functions */ int srdp_open(struct srdp_info *info) { /* opens the socket, adds it to the global select table, returns 0 if ok, -1 if error */ int fd; size_t length; struct srdp_infolist *ti; info->myaddr.sin_port = htons(info->localport); info->myaddr.sin_family = AF_INET; info->myaddr.sin_addr.s_addr = htonl(INADDR_ANY); if ((fd = socket(PF_INET, SOCK_DGRAM, 0))<0) return -1; if (bind(fd, (struct sockaddr *)&(info->myaddr), sizeof(struct sockaddr))<0) { close(fd); return -1; } length = sizeof(info->myaddr); if (getsockname(fd, (struct sockaddr *)&(info->myaddr), &length)<0) { close(fd); return -1; } info->sockfd = fd; info->localport = ntohs(info->myaddr.sin_port); if (info->max_rcv_lst_size == 0) info->max_rcv_lst_size = SRDP_DEFL_MAX_RCV; if (info->max_snt_lst_size == 0) info->max_snt_lst_size = SRDP_DEFL_MAX_SNT; if (info->max_packet_len == 0) info->max_packet_len = SRDP_DEFL_MAX_PACKET; if (info->max_packet_len>MAXPACKETLEN) info->max_packet_len = MAXPACKETLEN; if (info->curr_1_time == 0) info->curr_1_time = SRDP_CURR_1_TM; if (info->curr_nxt_time == 0) info->curr_nxt_time = SRDP_CURR_NXT_TM; if (info->miss_time == 0) info->miss_time = SRDP_MISS_TM; if (info->alive_time == 0) info->alive_time = SRDP_ALIVE_TM; if (info->oldest_time == 0) info->oldest_time = SRDP_OLDEST_TM; if (info->broken_time == 0) info->broken_time = SRDP_BROKEN_TM; info->flags&= (SRDP_FL_NOSELECT|SRDP_FL_SELRETURN); info->recvlist_first = NULL; info->recvlist_last = NULL; info->sentlist_first = NULL; info->sentlist_last = NULL; info->recvlist_size = 0; info->sentlist_size = 0; info->current_seq = 0; info->next_seq = 1; info->recvdarray = (srdp_u32 *)malloc(SRDP_RARRSIZE<<2); if (info->recvdarray == NULL) { close(fd); return -1; } memset((char *)info->recvdarray, 0, SRDP_RARRSIZE<<2); info->rarr_seqbase = 1; info->rarr_start = 0; info->lastheard.tv_sec = 0L; info->lastheard.tv_usec = 0L; info->lastsentcurrent.tv_sec = 0L; info->lastsentcurrent.tv_usec = 0L; info->lastsentsomething.tv_sec = 0L; info->lastsentsomething.tv_usec = 0L; info->lastsentlist.tv_sec = 0L; info->lastsentlist.tv_usec = 0L; info->lastsentoldest.tv_sec = 0L; info->lastsentoldest.tv_usec = 0L; /* wakeup{1,2} is initialized and used by srdp_select() */ info->current_sent = 0; ti = (struct srdp_infolist *)malloc(sizeof(struct srdp_infolist)); if (ti == NULL) { close(fd); return -1; } ti->theinfo = info; ti->next = srdp_allinfos; srdp_allinfos = ti; if (cansend(info)) srdp_sendalive(info); return 0; } int srdp_select(int width, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { /* does a select on the given fds + on any open sockets, and updates the SRDP_FL_READY flag on any sockets that are ready for reading (which actually means there are data chunks already read); returns when the timeout expires or there is anythign to read/write/err on the given fd_sets, or there is somethign to read on one of the open connections which has not been marked SRDP_FL_NOSELECT; the result and the resulting fd_sets don't include the fds from the connection */ struct srdp_info *info; struct srdp_infolist *infol, *nextinfo; struct timeval now, wakeup, mytimeout, returntime; fd_set myreadfds, orgreadfds, orgwritefds, orgexceptfds; int res, returnit; infol = srdp_allinfos; while (infol != NULL) { if ((infol->theinfo->flags&(SRDP_FL_READY|SRDP_FL_NOSELECT|SRDP_FL_CLOSED)) == SRDP_FL_READY) { if (readfds != NULL) FD_ZERO(readfds); if (writefds != NULL) FD_ZERO(writefds); if (exceptfds != NULL) FD_ZERO(exceptfds); return 0; } infol = infol->next; } if (readfds != NULL) orgreadfds =*readfds; else FD_ZERO(&orgreadfds); if (writefds != NULL) orgwritefds =*writefds; else FD_ZERO(&orgwritefds); if (exceptfds != NULL) orgexceptfds =*exceptfds; else FD_ZERO(&orgexceptfds); gettimeofday(&now, NULL); returntime = now; if (timeout != NULL) { returntime.tv_usec += timeout->tv_usec; returntime.tv_sec += timeout->tv_sec; if (returntime.tv_usec>1000000L) { returntime.tv_sec++; returntime.tv_usec -= 1000000L; } } else returntime.tv_sec += 604800L; while (1) { myreadfds = orgreadfds; wakeup.tv_sec = 0; /* update all wakeup values + find earliest one */ infol = srdp_allinfos; while (infol != NULL) { info = infol->theinfo; if ((info->flags&SRDP_FL_CLOSED) != 0) { infol = infol->next; continue; } if (info->sockfd >= width) width = info->sockfd+1; FD_SET(info->sockfd, &myreadfds); if (readfds != NULL) FD_CLR(info->sockfd, readfds); if (writefds != NULL) FD_CLR(info->sockfd, writefds); if (exceptfds != NULL) FD_CLR(info->sockfd, exceptfds); if (cansend(info)) { if (info->current_sent) { info->wakeup1 = info->lastsentcurrent; if (info->lastsentcurrent.tv_sec == 0) info->wakeup1 = now; info->wakeup1.tv_sec += info->curr_nxt_time; } else { info->wakeup1 = info->lastheard; if (info->lastheard.tv_sec == 0) info->wakeup1 = now; info->wakeup1.tv_sec += info->curr_1_time; } info->wakeup2 = info->lastsentsomething; if (info->lastsentsomething.tv_sec == 0) info->wakeup2 = now; info->wakeup2.tv_sec += info->alive_time; if (wakeup.tv_sec == 0 || wakeup.tv_sec>info->wakeup1.tv_sec || (wakeup.tv_sec == info->wakeup1.tv_sec && wakeup.tv_usec>info->wakeup1.tv_usec)) wakeup = info->wakeup1; if (wakeup.tv_sec == 0 || wakeup.tv_sec>info->wakeup2.tv_sec || (wakeup.tv_sec == info->wakeup2.tv_sec && wakeup.tv_usec>info->wakeup2.tv_usec)) wakeup = info->wakeup2; } infol = infol->next; } if (wakeup.tv_sec == 0) { wakeup = now; wakeup.tv_sec += 604800L; } else if (wakeup.tv_sectv_sectv_sec == mytimeout.tv_sec && timeout->tv_usectheinfo; nextinfo = infol->next; if ((info->flags&SRDP_FL_CLOSED) != 0) { infol = infol->next; continue; } if (FD_ISSET(info->sockfd, &myreadfds)) { if (readfds != NULL) FD_CLR(info->sockfd, readfds); res--; srdp_stufftoread(info); } else { if (cansend(info) && (now.tv_sec>info->wakeup1.tv_sec || (now.tv_sec == info->wakeup1.tv_sec && now.tv_usec >= info->wakeup1.tv_usec))) srdp_sendcurr(info, 0); if ((info->flags&(SRDP_FL_SELRETURN|SRDP_FL_NOSELECT)) == SRDP_FL_SELRETURN) returnit++; } info->wakeup2 = info->lastsentsomething; if (info->lastsentsomething.tv_sec == 0) info->wakeup2 = now; info->wakeup2.tv_sec += info->alive_time; if (cansend(info) && (now.tv_sec>info->wakeup2.tv_sec || (now.tv_sec == info->wakeup2.tv_sec && now.tv_usec >= info->wakeup2.tv_usec))) srdp_sendalive(info); if (info->lastheard.tv_sec+info->broken_time <= now.tv_sec) info->flags|= SRDP_FL_BROKEN; else info->flags&=~SRDP_FL_BROKEN; if ((info->flags&(SRDP_FL_READY|SRDP_FL_NOSELECT)) == SRDP_FL_READY) returnit++; infol = nextinfo; } if (returnit || res>0 || now.tv_sec>returntime.tv_usec || (now.tv_sec == returntime.tv_sec && now.tv_usec >= returntime.tv_usec)) return res; if (readfds != NULL) *readfds = orgreadfds; if (readfds != NULL) *writefds = orgwritefds; if (exceptfds != NULL) *exceptfds = orgexceptfds; gettimeofday(&now, NULL); } } int srdp_read(struct srdp_info *info, struct srdp_chunk *chunk, int csize) { /* reads one data chunk (from the srdp buffers) and returns 1 if ok, or 0 if there was none to read, or 2 if the packet was truncated, -1 if the connection is closed; updates SRDP_FL_READY */ int ret = 1; struct srdp_chunk_list *cl; if (info->flags&SRDP_FL_CLOSED) return -1; if (info->recvlist_first == NULL || csizerecvlist_first->chunk->u.noseq.len)) { memcpy((char *)chunk, (char *)info->recvlist_first->chunk, csize); ret = 2; } else memcpy((char *)chunk, (char *)info->recvlist_first->chunk, ntohl(info->recvlist_first->chunk->u.noseq.len)); info->recvlist_size -= ntohl(chunk->u.noseq.len)+CHUNKLIST_EXTRA; cl = info->recvlist_first; info->recvlist_first = cl->next; if (cl->next) { cl->next->prev = NULL; info->flags|= SRDP_FL_READY; } else { info->recvlist_last = NULL; info->flags&=~SRDP_FL_READY; } free(cl); return ret; } int srdp_write(struct srdp_info *info, struct srdp_chunk *chunk) { /* sends out a data chunk; returns -1 if error, 0 if ok */ struct srdp_chunk *c; struct timeval now; struct srdp_chunk_list *cl, *ncl; if ((info->flags&SRDP_FL_CLOSED) != 0) { errno = ENOTCONN; return -1; } if (ntohl(chunk->u.sequenced.len) < SRDP_HEADER_SEQ_LEN || ntohl(chunk->u.sequenced.len) > info->max_packet_len) { errno = EMSGSIZE; return -1; } c = (struct srdp_chunk *)malloc(ntohl(chunk->u.sequenced.len)); if (c == NULL) { errno = ENOMEM; return -1; } ncl = (struct srdp_chunk_list *)malloc(sizeof (struct srdp_chunk_list)); if (ncl == NULL) { errno = ENOMEM; return -1; } initchunk(chunk); chunk->u.sequenced.seq = htonl(info->next_seq); info->next_seq++; memcpy((char *)c, (char *)chunk, ntohl(chunk->u.sequenced.len)); if (cansend(info)) sendpacket(info, (unsigned char *)chunk, ntohl(chunk->u.sequenced.len)); gettimeofday(&now, NULL); info->lastsentsomething = now; while (info->sentlist_size + CHUNKLIST_EXTRA + ntohl(chunk->u.sequenced.len) > info->max_snt_lst_size) { cl = info->sentlist_first; if (cl->next != NULL) cl->next->prev = NULL; info->sentlist_first = cl->next; info->sentlist_size -= cl->lsize; if (info->sentlist_last == cl) info->sentlist_last = NULL; free(cl->chunk); free(cl); } ncl->prev = info->sentlist_last; ncl->next = NULL; ncl->lsize = CHUNKLIST_EXTRA+ntohl(chunk->u.sequenced.len); ncl->chunk = c; info->sentlist_size += ncl->lsize; if (ncl->prev) ncl->prev->next = info->sentlist_last = ncl; else info->sentlist_last = info->sentlist_first = ncl; return 0; } void srdp_synch(struct srdp_info *info) { if (cansend(info)) srdp_sendcurr(info, 1); } void srdp_close(struct srdp_info *info, char *message) { /* attempts to gracefully close the connection; may block for a few seconds */ /* we should actually send out that message... */ struct srdp_chunk *c; struct timeval now, next, timeout; fd_set readfds; int times = 0, acked = 0, sres, readsize; size_t fromlen; struct sockaddr_in from; if (cansend(info)) { c = (struct srdp_chunk *)packbuf; initchunk(c); c->u.noseq.type = SRDP_CLOSE; c->u.noseq.len = htonl(8); sendpacket(info, packbuf, 8); gettimeofday(&next, NULL); while (1) { gettimeofday(&now, NULL); if (now.tv_sec>next.tv_sec || (now.tv_sec == next.tv_sec && now.tv_usec >= next.tv_usec)) { times++; if (times>4) break; sendpacket(info, packbuf, 8); next.tv_sec += 5; } timeout = next; if (timeout.tv_usecsockfd, &readfds); sres = select(info->sockfd+1, &readfds, NULL, NULL, &timeout); if (sres) { readsize = recvfrom(info->sockfd, (char *)readbuf, MAXPACKETLEN, 0, (struct sockaddr *)&from, &fromlen); if (readsize >= SRDP_HEADER_USEQ_LEN && from.sin_port == htons(info->remoteport) && info->remote.s_addr == from.sin_addr.s_addr && (readbuf[3] == SRDP_DROP || readbuf[3] == SRDP_CLOSE)) { acked++; break; } } } if (!acked) { c->u.noseq.type = SRDP_DROP; sendpacket(info, packbuf, 8); sendpacket(info, packbuf, 8); sendpacket(info, packbuf, 8); } } closeandforget(info); } void srdp_drop(struct srdp_info *info, char *message) { /* drops the connection; will not block */ struct srdp_chunk *c; if (cansend(info)) { c = (struct srdp_chunk *)packbuf; initchunk(c); c->u.noseq.type = SRDP_DROP; c->u.noseq.len = htonl(8); sendpacket(info, packbuf, 8); sendpacket(info, packbuf, 8); sendpacket(info, packbuf, 8); } closeandforget(info); } /* add a command to send out a ping */ #ifdef DEBUG /* xxx debugging stuff */ void srdp_infos(struct srdp_info *info) { int idx; srdp_u32 refto, msk; if (info->recvlist_first == NULL) printf("nothing on the recvdlist\n"); else printf("recvdlist goes from %ld to %ld\n", ntohl(info->recvlist_first->chunk->u.sequenced.seq), ntohl(info->recvlist_last->chunk->u.sequenced.seq)); printf("recvlist size = %d\n", info->recvlist_size); if (info->sentlist_first == NULL) printf("nothing on the sentdlist\n"); else printf("sentlist goes from %ld to %ld\n", ntohl(info->sentlist_first->chunk->u.sequenced.seq), ntohl(info->sentlist_last->chunk->u.sequenced.seq)); printf("sentlist size = %d\n", info->sentlist_size); printf("current_seq = %ld, next_seq = %ld, rarr_seqbase = %ld, rarr_start = %d\n", info->current_seq, info->next_seq, info->rarr_seqbase, info->rarr_start); idx = (info->rarr_start+((info->current_seq-info->rarr_seqbase)>>5))& SRDP_RARRMASK; msk = 1<<((info->current_seq-info->rarr_seqbase)&0x1f); refto = info->current_seq; printf("misslst is: "); if (!refto) printf("empty"); else do { if ((info->recvdarray[idx]&msk) == 0) printf(" %ld", refto); if (msk>1) msk = msk>>1; else { msk = (srdp_u32)0x80000000; idx = (idx-1)&SRDP_RARRMASK; } refto--; } while (refto >= info->rarr_seqbase); printf("\n"); } int srdp_showarr(struct srdp_info *info) { int idx; srdp_u32 refto, msk; idx = (info->rarr_start+((info->current_seq-info->rarr_seqbase)>>5))& SRDP_RARRMASK; msk = 1<<((info->current_seq-info->rarr_seqbase)&0x1f); refto = info->current_seq; printf("recvdarray is %.4lx %.4lx %.4lx %.4lx\n", info->recvdarray[0], info->recvdarray[1], info->recvdarray[2], info->recvdarray[3]); printf("misslst is: "); if (!refto) printf("empty"); else do { if ((info->recvdarray[idx]&msk) == 0) printf(" %ld", refto); if (msk>1) msk = msk>>1; else { msk = (srdp_u32)0x80000000; idx = (idx-1)&SRDP_RARRMASK; } refto--; } while (refto >= info->rarr_seqbase); printf("\n"); return 0; } #endif utalk-1.0.1.beta.orig/termcap.c0100644000076600007650000002227406376753245015633 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp Copyright (C) 1995 Roger Espel Llima termcap.c Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include #include #include #include #include "globals.h" #include "util.h" #include "termcap.h" static char *termtype, termcap[2048], *tc, capabilities[2048]; static char termcapbuf[2000]; static int tcb = 0; char *t_cm, *t_cl, *t_ce, *t_me, *t_mr, *t_md, *t_us, *t_cs, *t_sr, *t_sf, *t_le, *t_do, *t_nd, *t_up, *t_bl; int ansi_cs = 0; volatile int xcursor = -1, ycursor = -1; /* absolute; -1, -1 = unknown */ static unsigned char **the_screen, **the_attrs; /* the_screen[y] = string for the y-th line, followed by spaces the_screen[y][x] = character at x,y the_attrs[y][x] = attribute at x,y the_screen[y][0] = number of chars in the line */ static int myputchar(int c) { if (tcb>1998) { write(TTYWRITE, termcapbuf, tcb); tcb = 0; } termcapbuf[tcb++] = (char )c; return 0; } static void mywrite(char *s, int l) { if (l+tcb>1998) { write(TTYWRITE, termcapbuf, tcb); tcb = 0; } if (l>0) memcpy(termcapbuf+tcb, s, l); tcb += l; } void flush_term(void) { if (tcb>0) { write(TTYWRITE, termcapbuf, tcb); tcb = 0; } } void putcap(char *s) { tputs(s, 0, myputchar); } static void empty_line(int n) { memset((char *)the_attrs[n], 0, cols+2); memset((char *)the_screen[n], ' ', cols+2); the_screen[n][0] = 0; } static void alloc_blank_line(int n) { /* alloc line and put it in place */ the_screen[n] = mymalloc(cols+8); the_attrs[n] = mymalloc(cols+8); empty_line(n); } void alloc_termcap_screen(void) { /* alloc the whole screen and blank it */ int n; the_screen = (void *)mymalloc((lines+8)*sizeof(char *)); the_attrs = (void *)mymalloc((lines+8)*sizeof(char *)); for (n = 1; n <= lines; ++n) alloc_blank_line(n); } static void empty_screen(void) { int n; for (n = 1; n <= lines; ++n) empty_line(n); } void init_termcap(void) { /* check we're runnign on a tty, initialize the reading of the termcap database, read all the capabilities we'll be using (except for "li" and "co", which are read elsewhere */ if (!isatty(0)) barf("I can only run on a tty, sorry"); if ((termtype = (char *)getenv("TERM")) == NULL) barf("No terminal type set"); if (tgetent(termcap, termtype)<1) barf("No termcap info for your terminal"); tc = capabilities; if ((t_cm = tgetstr("cm", &tc)) == NULL) barf("Can't find a way to move the cursor around with your terminal"); if ((t_cl = tgetstr("cl", &tc)) == NULL) barf("Can't find a way to clear the screen with your terminal"); if ((t_ce = tgetstr("ce", &tc)) == NULL) barf("Can't find a way to clear to end of line with your terminal"); if ((t_cs = tgetstr("cs", &tc)) == NULL) { if (strncmp(termtype, "xterm", 5) == 0 || strncmp(termtype, "vt100", 5) == 0) ansi_cs = 1; } t_sr = tgetstr("sr", &tc); if ((t_sf = tgetstr("sf", &tc)) == NULL) { t_sf = tc; (*tc++) = '\n'; (*tc++) = 0; } t_le = tgetstr("le", &tc); t_do = tgetstr("do", &tc); t_nd = tgetstr("nd", &tc); t_up = tgetstr("up", &tc); t_bl = tgetstr("bl", &tc); if ((t_me = tgetstr("me", &tc)) != NULL) { if ((t_mr = tgetstr("mr", &tc)) == NULL) t_mr = t_me; if ((t_md = tgetstr("md", &tc)) == NULL) t_md = t_me; if ((t_us = tgetstr("us", &tc)) == NULL) t_us = t_me; } else if ((t_me = tgetstr("se", &tc)) != NULL && (t_mr = tgetstr("so", &tc)) != NULL) { t_md = t_mr; t_us = tc; (*tc++) = '\0'; } else { t_me = t_md = t_mr = t_us = tc; (*tc++) = '\0'; } } void beep(void) { if (beep_on && t_bl) putcap(t_bl); } void cleareol(void) { if (xcursor<0 || ycursor<0 || xcursor>cols || ycursor>lines) cleanupexit(2, "Error: can't clear to eol from unknown cursor position"); putcap(t_ce); if (xcursor <= (int)the_screen[ycursor][0]) the_screen[ycursor][0] = xcursor-1; memset((char *)&the_screen[ycursor][xcursor], ' ', cols+3-xcursor); memset((char *)&the_attrs[ycursor][xcursor], 0, cols+3-xcursor); } void putscreen(char *s, int n, char attr) { /* writes to the screen, assuming the chars are all printable and nothing is going to wrap; remmebers them with the attribute attr */ int c; if (xcursor<0 || ycursor<0 || xcursor>cols || ycursor>lines) cleanupexit(2, "Error: can't write to screen from unknown cursor position"); mywrite(s, n); for (c = 0; ccols) { xcursor = -1; ycursor = -1; } } void gotoxy(int x, int y) { /* moves the cursor to position x, y (assumed to fit), updating xcursor and ycursor; attempts to be smart and use the current values of xcursor/ycursor if they are adjacent */ if (x == xcursor+1 && x>0 && y == ycursor && t_nd != NULL) putcap(t_nd); else if (x == xcursor-1 && y == ycursor && t_le != NULL) putcap(t_le); else if (x == xcursor && y == ycursor+1 && y>0 && t_do != NULL) putcap(t_do); else if (x == xcursor && y == ycursor-1 && t_up != NULL) putcap(t_up); else if (x != xcursor || y != ycursor) putcap((char *)tgoto(t_cm, x-1, y-1)); xcursor = x; ycursor = y; } void backspace(void) { if (xcursor<0 || ycursor<0 || xcursor>cols || ycursor>lines) cleanupexit(2, "Error: can't backspace from unknown cursor position"); if (xcursor>1) { if (t_le != NULL) { putcap(t_le); myputchar(' '); putcap(t_le); xcursor--; } else { gotoxy(xcursor-1, ycursor); myputchar(' '); gotoxy(xcursor-1, ycursor); } } } void clearscreen(void) { /* clears the screen and puts the cursor at 1,1 */ putcap(t_cl); xcursor = ycursor = -1; empty_screen(); gotoxy(1, 1); } void redrawlines(int y1, int y2) { /* redraws the screen from line y1 to y2, using the saved info */ int l, s, ls, x = xcursor, y = ycursor; char sattr; normal(); for (l = y1; l <= y2; ++l) { xcursor = ycursor = -1; gotoxy(1, l); sattr = 0; ls = s = 1; while(s <= (int)the_screen[l][0]) { if (sattr == the_attrs[l][s]) s++; else { if (s != ls) mywrite((char *)&the_screen[l][ls], s-ls); ls = s; if ((the_attrs[l][s]&sattr) == sattr) { if ((sattr&ATTR_BOLD) == 0 && (the_attrs[l][s]&ATTR_BOLD) != 0) setbold(); if ((sattr&ATTR_UNDERLINED) == 0 && (the_attrs[l][s]&ATTR_UNDERLINED) != 0) setunder(); if ((sattr&ATTR_INVERSE) == 0 && (the_attrs[l][s]&ATTR_INVERSE) != 0) setinv(); } else { normal(); if ((the_attrs[l][s]&ATTR_BOLD) != 0) setbold(); if ((the_attrs[l][s]&ATTR_UNDERLINED) != 0) setunder(); if ((the_attrs[l][s]&ATTR_INVERSE) != 0) setinv(); } sattr = the_attrs[l][s]; } } if (s != ls) mywrite((char *)&the_screen[l][ls], s-ls); if (sattr) normal(); putcap(t_ce); } xcursor = ycursor = -1; if (x != -1 && y != -1) gotoxy(x, y); } void redraw(void) { /* redraws the whole screen using the saved info */ redrawlines(1, lines); } void do_cs(int y1, int y2) { /* assumes either is possible, and does a t_cs or an ansi one */ static char temp[16]; if (ansi_cs) { sprintf(temp, "\e[%d;%dr", y1, y2); mywrite(temp, strlen(temp)); } else putcap((char *)tgoto(t_cs, y2-1, y1-1)); } void scrolldown(int y1, int y2) { /* scrolls the lines from y1 to y2 down by one line */ int n; free(the_screen[y2]); free(the_attrs[y2]); for (n = y2; n>y1; n--) { the_screen[n] = the_screen[n-1]; the_attrs[n] = the_attrs[n-1]; } alloc_blank_line(y1); if (y1 == y2) { gotoxy(1, y2); cleareol(); } else if (t_sr != NULL && ((y1 == 1 && y2 == lines) || t_cs != NULL || ansi_cs)) { if (y1>1 || y21 || y2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include #include #include #include "globals.h" #include "util.h" #include "termio.h" #include "termcap.h" #ifdef USE_SGTTY #include #else #include #endif #ifdef TIOCGWINSZ struct winsize wsz; #endif #ifdef USE_SGTTY struct sgttyb term, term0; struct tchars tch, tch0; struct ltchars lch, lch0; #else struct termios term, term0; #endif void get_screensize(void) { /* gets the best estimate of the screen size in 'cols' and 'lines'; requires termcap to be initialized */ char *vr; #ifdef TIOCGWINSZ if (ioctl(TTYWRITE, TIOCGWINSZ, &wsz)<0 || wsz.ws_row<1 || wsz.ws_col<1) { #endif lines = ((vr = getenv("LINES")) ? atoi(vr) : 0); cols = ((vr = getenv("COLUMNS")) ? atoi(vr) : 0); if (lines<1 || cols<1) { if ((lines = tgetnum("li"))<1 || (cols = tgetnum("co"))<1) { lines = 24; cols = 80; } } #ifdef TIOCGWINSZ } else { lines = wsz.ws_row; cols = wsz.ws_col; } #endif if (lines<5 || cols<4) cleanupexit(1, "window too small"); if (cols>250) cols = 250; if (lines>250) lines = 250; } void init_termio(void) { /* saves the tty modes and sets the new ones */ #ifdef USE_SGTTY if (ioctl(TTYREAD, TIOCGETP, &term)<0 || ioctl(TTYREAD, TIOCGETC, &tch)<0 || ioctl(TTYREAD, TIOCGLTC, &lch)<0) { perror("sgtty get ioctl"); exit(1); } term0 = term; tch0 = tch; lch0 = lch; term.sg_flags|= CBREAK; term.sg_flags&= ~ECHO & ~CRMOD; memset(&tch, -1, sizeof(tch)); memset(&lch, -1, sizeof(lch)); tch.t_intrc = (char)3; tch.t_quitc = (char)28; lch.t_suspc = (char)26; if (ioctl(TTYREAD, TIOCSETP, &term)<0 || ioctl(TTYREAD, TIOCSETC, &tch)<0 || ioctl(TTYREAD, TIOCSLTC, &lch)<0) { perror("sgtty set ioctl"); exit(1); } #else if (tcgetattr(TTYREAD, &term)<0) { perror("tcgetattr"); exit(1); } term0 = term; term.c_lflag &= ~ECHO & ~ICANON; term.c_oflag &= ~OPOST; term.c_cc[VTIME] = (char)0; term.c_cc[VMIN] = (char)1; term.c_cc[VSTOP] = (char)0; term.c_cc[VSTART] = (char)0; term.c_cc[VQUIT] = (char)28; term.c_cc[VINTR] = (char)3; term.c_cc[VSUSP] = (char)26; #ifdef VREPRINT term.c_cc[VREPRINT] = (char)0; #endif #ifdef VDISCARD term.c_cc[VDISCARD] = (char)0; #endif #ifdef VLNEXT term.c_cc[VLNEXT] = (char)0; #endif #ifdef VDSUSP term.c_cc[VDSUSP] = (char)0; #endif if (tcsetattr(TTYREAD, TCSANOW, &term)<0) { perror("tcsetattr"); exit(1); } #endif ++screen_inited; } #define SET_RAW 0 #define SET_COOKED 1 void settty(int how, int drain) { /* sets the tty mode to 'raw' (the mode we use) or 'cooked' (the mode the tty was originally in) according to 'how' and using the saved values; if 'drain' is set, uses TCSADRAIN (if using the POSIX interface) */ #ifdef USE_SGTTY ioctl(TTYREAD, TIOCSETP, how == SET_RAW ? &term : &term0); ioctl(TTYREAD, TIOCSETC, how == SET_RAW ? &tch : &tch0); ioctl(TTYREAD, TIOCSLTC, how == SET_RAW ? &lch : &lch0); #else tcsetattr(TTYREAD, drain ? TCSADRAIN : TCSANOW, how == SET_RAW ? &term : &term0); #endif } utalk-1.0.1.beta.orig/utalk.c0100644000076600007650000000750306376753253015315 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp Copyright (C) 1995 Roger Espel Llima utalk.c Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include "srdp.h" #include "globals.h" #include "termcap.h" #include "termio.h" #include "util.h" #include "signal.h" #include "screen.h" #include "kbd.h" #include "comm.h" #include "rc.h" #include #include #include #include #include #include #ifdef USE_SGTTY #include #else #include #endif #ifdef _AIX #include #endif #ifdef TIOCGWINSZ struct winsize wsz; #endif #ifdef USE_SGTTY struct sgttyb term, term0; struct tchars tch, tch0; struct ltchars lch, lch0; #else struct termios term, term0; #endif int cols, lines; static void use(char *m) { if (m) fprintf(stderr, "utalk: %s; try utalk --help for help\n", m); else { fprintf(stderr, "Use: utalk [options] user[@host][#tty]\n" " or: utalk [options] !port@host\n" " or: utalk [options] -s port\n" " or: utalk [options] -c host port\n\n" "Options: -s, --server -- raw server connection\n" " -c, --client -- raw client connection\n" " -a, --announce-only -- send port number in announcement\n" " -7, --seven-bit -- run in 7-bit mode\n" " -8, --eight-bit -- run in 8-bit mode\n" ); } exit(1); } int main(int argc, char *argv[]) { int port = 0, raw = 0, minus = -1; char *host = NULL, *uh = NULL; srdp_u32 u32; srdp_u16 u16; srdp_u8 u8; u32 = minus; if (sizeof(srdp_u32) != 4 || u32 <= 0) barf("srdp_u32 isn't an unsigned 32-bit type, please edit srdpdata.h and recompile"); u16 = minus; if (sizeof(srdp_u16) != 2 || u16 <= 0) barf("srdp_u16 isn't an unsigned 16-bit type, please edit srdpdata.h and recompile"); u8 = minus; if (sizeof(srdp_u8) != 1 || u8 <= 0) barf("srdp_u8 isn't an unsigned 8-bit type, please edit srdpdata.h and recompile"); read_rc(); while (argc>1 && argv[1][0] == '-') { if (strcmp(argv[1], "-7") == 0 || strcmp(argv[1], "--seven-bit") == 0) { eight_bit_clean = 0; argv++; argc--; } else if (strcmp(argv[1], "-8") == 0 || strcmp(argv[1], "--eight-bit") == 0) { eight_bit_clean = 1; argv++; argc--; } else if (strcmp(argv[1], "-s") == 0 || strcmp(argv[1], "--server") == 0) { if (argc <= 2) use("missing argument for -s"); raw = 1; port = atoi(argv[2]); argv += 2; argc -= 2; } else if (strcmp(argv[1], "-c") == 0 || strcmp(argv[1], "--client") == 0) { raw = 2; if (argc <= 3) use("missing argument for -c"); host = argv[2]; port = atoi(argv[3]); argv += 3; argc -= 3; } else if (strcmp(argv[1], "-a") == 0 || strcmp(argv[1], "--announce-only") == 0) { raw = -1; argv++; argc--; } else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { use(NULL); } else use("unrecognized option"); } if (argc < 2 && !raw) use(NULL); allsignals(); init_termcap(); get_screensize(); init_termio(); alloc_termcap_screen(); clearscreen(); init_screen(); if (!raw && argv[1][0] == '!' && argv[1][1] >= '0' && argv[1][1] <= '9') { raw = 2; port = atoi(&argv[1][1]); host = argv[1]; while (*host && *host != '@') host++; if (*host == '@') host++; } else if (raw <= 0) { uh = argv[1]; if (uh[0] == '!') uh++; } init_comm(raw, host, port, uh); gotoxy(1, 2); main_loop(); cleanupexit(1, ""); exit(0); } utalk-1.0.1.beta.orig/util.c0100644000076600007650000000335506376753254015154 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp Copyright (C) 1995 Roger Espel Llima util.c Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #include #include #include "globals.h" #include "termio.h" #include "termcap.h" #include "comm.h" #include "util.h" void barf(char *m) { fprintf(stderr, "%s\n", m); exit(1); } void cleanupexit(int n, char *m) { quick_clear_invite(); if (connected) c_close(); if (screen_inited) { normal(); xcursor = ycursor = -1; gotoxy(0, lines-1); cleareol(); flush_term(); settty(SET_COOKED, 1); } if (m) fprintf(stderr, "%s\n", m); exit(n); } void *mymalloc(int n) { char *r; r = (void *)malloc(n); if (r == NULL) cleanupexit(1, "Insufficient memory"); return r; } void *myrealloc(void *p, int n) { char *r; r = (void *)realloc(p, n); if (r == NULL) cleanupexit(1, "Insufficient memory"); return r; } unsigned char *getword(unsigned char **s) { /* returns the beginning of the word and sets the pointer to after it or to NULL if none after; will return NULL if no word at all; puts a '\0' after the word */ unsigned char *r =*s; while (*r == ' ' || *r == 9 || *r == 10 || *r == 13) r++; if (*r == 0) { *s = NULL; return NULL; } *s = r; while (**s != ' ' && **s != 0 && **s != 9 && **s != 10 && **s != 13) (*s)++; if (**s == 0) { *s = NULL; return r; } **s = 0; (*s)++; while (**s == ' ' || **s == 9 || **s == 10 || **s == 13) (*s)++; if (**s == 0) *s = NULL; return r; } utalk-1.0.1.beta.orig/comm.h0100644000076600007650000000735706376753224015142 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp Copyright (C) 1995 Roger Espel Llima comm.h Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef COMM_H #define COMM_H #include #include #include "srdp.h" #define READBUFSIZE 2084 #define OUTBUFSIZE 2048 #define SENDAFTER 440 /* send a packet when buffered data exceeds..*/ #define MAXBUFDELAY 250000 /* max delay in microseconds for buffering */ #define UTALK_DATA 2 #define UTALK_TOPIC 4 #define UTALK_REVISION 1 #define ODAEMON 0 #define NDAEMON 1 #define BOTH 2 #define NAME_SIZE 9 #define TTY_SIZE 16 typedef struct { srdp_u16 sin_family; srdp_u16 sin_port; struct in_addr sin_addr; char sin_zero[8]; } BSD42_SOCK; struct mesg { int daemon; /* type of daemon */ char type; /* type of message */ char local_name[NAME_SIZE]; char remote_name[NAME_SIZE]; char remote_tty[TTY_SIZE]; /* struct sockaddr_in addr; struct sockaddr_in ctl_addr; xxx these get filled automatically */ srdp_u32 id; }; struct answer { int daemon; /* type of daemon */ char type; char answer; srdp_u32 id; BSD42_SOCK addr; }; /* Control Message structure for earlier than BSD4.2 */ struct old_msg { char type; char l_name[NAME_SIZE]; char r_name[NAME_SIZE]; char filler; srdp_u32 id_num; srdp_u32 pid; char r_tty[TTY_SIZE]; BSD42_SOCK addr; BSD42_SOCK ctl_addr; }; /* Control Response structure for earlier than BSD4.2 */ struct old_reply { char type; char answer; srdp_u16 filler; srdp_u32 id_num; BSD42_SOCK addr; }; /* Control Message structure for BSD4.2 */ struct new_msg { unsigned char vers; char type; char filler[2]; srdp_u32 id_num; BSD42_SOCK addr; BSD42_SOCK ctl_addr; srdp_u32 pid; char l_name[NAME_SIZE]; char l_name_filler[3]; char r_name[NAME_SIZE]; char r_name_filler[3]; char r_tty[TTY_SIZE]; }; /* Control Response structure for BSD4.2 */ struct new_reply { unsigned char vers; char type; char answer; char filler; srdp_u32 id_num; BSD42_SOCK addr; }; #define NTALKD_VERSION 1 /* dgram types */ #define LEAVE_INVITE 0 /* leave an invitation (local) */ #define LOOK_UP 1 /* look up an invitation (remote) */ #define DELETE 2 /* delete erroneous invitation (remote) */ #define ANNOUNCE 3 /* ring a user (remote) */ #define DELETE_INVITE 4 /* delete my invitation (local) */ /* answer values */ #define SUCCESS 0 /* operation completed properly */ #define NOT_HERE 1 /* callee not logged in */ #define FAILED 2 /* operation failed for unexplained reason */ #define MACHINE_UNKNOWN 3 /* caller's machine name unknown */ #define PERMISSION_DENIED 4 /* callee's tty doesn't permit announce */ #define UNKNOWN_REQUEST 5 /* request has invalid type value */ #define BADVERSION 6 /* request has invalid protocol version */ #define BADADDR 7 /* request has invalid addr value */ #define BADCTLADDR 8 /* request has invalid ctl_addr value */ extern void init_comm(int raw, char *h, int p, char *uh); extern void quick_clear_invite(void); extern void main_loop(void); extern void c_write_char(int line, int col, unsigned char ch); extern void c_write_string(int line, int col, unsigned char *s, int len); extern void c_set_cursor(int line, int col); extern void c_shorten_lline(int line, int len); extern void c_send_beep(int line, int col); extern void c_resync(void); extern void c_send_topic(char *s); extern void c_close(void); #endif utalk-1.0.1.beta.orig/functions.h0100644000076600007650000000715206376753226016212 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp by Roger Espel Llima functions.h This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef FUNCTIONS_H #define FUNCTIONS_H #include "struct.h" extern int active_window; /* -1 = mine, read-write 0 = mine, read-only 1 = first other, read-only ... */ extern struct logical_line *current_lline; extern struct function the_functions[]; extern void f_self_insert(unsigned char c); extern void f_insert_in_place(unsigned char c); extern void f_tab(unsigned char c); extern void f_new_line(unsigned char c); extern void f_delete(unsigned char c); extern void f_delete_end_of_line(unsigned char c); extern void f_delete_beginning_of_line(unsigned char c); extern void f_delete_line(unsigned char c); extern void f_delete_word(unsigned char c); extern void f_delete_end_of_word(unsigned char c); extern void f_backspace(unsigned char c); extern void f_backspace_word(unsigned char c); extern void f_backward(unsigned char c); extern void f_forward(unsigned char c); extern void f_backward_word(unsigned char c); extern void f_forward_word(unsigned char c); extern void f_end_of_word(unsigned char c); extern void f_beginning_of_line(unsigned char c); extern void f_end_of_line(unsigned char c); extern void f_up(unsigned char c); extern void f_down(unsigned char c); extern void f_up_page(unsigned char c); extern void f_down_page(unsigned char c); extern void f_up_half_page(unsigned char c); extern void f_down_half_page(unsigned char c); extern void f_top_of_screen(unsigned char c); extern void f_middle_of_screen(unsigned char c); extern void f_bottom_of_screen(unsigned char c); extern void f_top_or_up_page(unsigned char c); extern void f_bottom_or_down_page(unsigned char c); extern void f_top(unsigned char c); extern void f_bottom(unsigned char c); extern void f_vi_goto_line(unsigned char c); extern void f_redisplay(unsigned char u); extern void f_resynch(unsigned char u); extern void f_next_window(unsigned char u); extern void f_nop(unsigned char u); extern void f_beep(unsigned char u); extern void f_vi_insert_mode(unsigned char c); extern void f_vi_command_mode(unsigned char c); extern void f_emacs_mode(unsigned char c); extern void f_quit(unsigned char c); extern void f_vi_add(unsigned char c); extern void f_vi_add_at_end_of_line(unsigned char c); extern void f_vi_insert_at_beginning_of_line(unsigned char c); extern void f_vi_escape(unsigned char c); extern void f_vi_open(unsigned char c); extern void f_vi_Open(unsigned char c); extern void f_vi_replace_char(unsigned char c); extern void f_vi_find_char(unsigned char c); extern void f_vi_reverse_find_char(unsigned char c); extern void f_vi_till_char(unsigned char c); extern void f_vi_reverse_till_char(unsigned char c); extern void f_vi_repeat_find(unsigned char c); extern void f_vi_reverse_repeat_find(unsigned char c); extern void f_vi_delete_find_char(unsigned char c); extern void f_vi_delete_reverse_find_char(unsigned char c); extern void f_vi_delete_till_char(unsigned char c); extern void f_vi_delete_reverse_till_char(unsigned char c); extern void f_vi_flip_case(unsigned char c); extern void f_quote_char(unsigned char c); extern void f_do_help(unsigned char c); extern void f_set_topic(unsigned char c); extern void f_do_command(unsigned char c); extern void f_test_menu(unsigned char c); /* xxx */ extern void f_test_entry(unsigned char c); /* xxx */ extern void f_test_selection(unsigned char c); /* xxx */ #endif utalk-1.0.1.beta.orig/globals.h0100644000076600007650000000153106376753227015621 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp by Roger Espel Llima globals.h This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef GLOBALS_H #define GLOBALS_H #include #define UTALK_VERSION "1.0.1 beta" extern int errno; extern struct user *users[300]; extern int eight_bit_clean; extern int cols, lines; extern int beep_on; extern int cannotrun; extern int connected; extern int wordwrap; extern int in_main_loop; extern int meta_esc; #define TTYREAD 0 #define TTYWRITE 1 extern volatile int screen_inited; extern volatile int mustdie; extern volatile int winch, newcols, newlines; extern volatile int mustredisplay; #endif utalk-1.0.1.beta.orig/kbd.h0100644000076600007650000000123706376753230014733 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp by Roger Espel Llima kbd.h This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef KBD_H #define KBD_H #include "struct.h" extern struct mode *current_mode, modes[N_MODES]; extern ftype force_next_func; extern int vi_prefix; extern void kbd_defaults(void); extern void keyboard(unsigned char c); extern char *new_binding(unsigned char *s, int insmode); extern struct mode *new_mode(unsigned char *s); #endif utalk-1.0.1.beta.orig/menu.h0100644000076600007650000000607206376753233015144 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp Copyright (C) 1995 Roger Espel Llima menu.h Started: 1 Dec 96 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef MENU_H #define MENU_H #define MENU_TEXT 1 /* print text & wait for Enter */ #define MENU_CHOOSE 2 /* print text & list, pick one */ #define MENU_ENTRY 3 /* print text, input line */ #define HELPFILE LIBDIR "/utalk.help" struct menu; typedef void (*menu_callback)(struct menu *); typedef void (*text_callback)(char *); struct menu { int type; menu_callback callback; /* gets called when done w/ the menu; is supposed to free the menu storage if it was malloced */ int preempted; /* set to 1 for the callback to see, if preempted */ char **text; /* text to show, terminated by a null line pointer, for types MENU_TEXT, MENU_CHOOSE, MENU_YESNO; for MENU_CHOOSE, the strings must be writable and be of the form " letter -- text " */ int *jumplines; /* line numbers to jump to with jump keys (or NULL)*/ char *jumpkeys; /* jump keys (or NULL) */ char *entrytxt; /* text to show (one line), for MENU_ENTRY */ char entry[256]; /* gets filled; can be initialized with a prompt; set promptlen to the len of the prompt and it won't be erased */ text_callback txtcallback; /* gets called when done entering text, with the text w/o the prompt, by the default text-mode callback; -1 means no selection */ int promptlen; int arrowat; /* line with the arrow, for selection menus; must be initialized, and read by the callback when done */ int arrowmin, arrowmax; /* must be initialized too... */ /* the rest is for internal use only */ char *buf; /* where to copy the entered string, used by the default text-mode callback */ int len; /* length of the entry, including prompt */ int showfrom; /* show starting from line n (start at 0) */ int showfromcol; /* show starting from col n (start at 0) */ int nlines, maxcols; /* size of text to show */ int wlines; /* text lines in menu window */ int wcols; /* text cols in menu window */ int more; /* if we're now showing the bottom */ /* xxx ... */ }; extern struct menu *the_menu; extern int menu_top, menu_bottom, menu_left, menu_right; /* valid only if the_menu != NULL */ extern void m_putscreen(char *s, int n, char attr); extern void m_cleareol(void); extern void m_gotoxy(int x, int y); extern void m_scrollup(int y1, int y2); extern void m_scrolldown(int y1, int y2); extern void m_backspace(void); extern void menu_keyboard(unsigned char c); extern void make_text_menu(char **s, int *jumplines, char *jumpkeys, int freeit); extern void input_text(char *text, char *prompt, char *buf, text_callback c); extern void redraw_menu(void); extern void do_help(void); extern void entertopic(void); extern void popup_command(char *prompt); #endif utalk-1.0.1.beta.orig/rc.h0100644000076600007650000000076606376753235014612 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp by Roger Espel Llima rc.h This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef RC_H #define RC_H struct toggle { char *name; int *var; }; char *resolve_alias(char *uh); void read_rc(void); char *do_rc_line(unsigned char *); #endif utalk-1.0.1.beta.orig/screen.h0100644000076600007650000000237706376753237015467 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp by Roger Espel Llima screen.h This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef SCREEN_H #define SCREEN_H #include "struct.h" #include "srdpdata.h" extern int active_buffers; extern void setstatus(struct user *u, char *s); extern void settopic(unsigned char *s); extern void doflags(struct user *u); extern void init_screen(void); extern void recalc_all(void); extern struct logical_line *find_lline(struct user *u, int n, srdp_u32 seq); extern int find_pline(struct user *u, struct logical_line *l); extern int get_plength(struct user *u, int *pl, int pos); extern void scroll_to_pline(struct user *u, int pl); extern void shorten_lline(struct user *u, struct logical_line *l, srdp_u32 seq, int len); extern void set_cursor(struct user *u, struct logical_line *l, srdp_u32 seq, int pos); extern void write_char(struct user *u, struct logical_line *l, srdp_u32 seq, int pos, unsigned char c); extern void pl_redraw_screen_zone(int y1, int y2); extern void pl_redraw_all(void); #endif utalk-1.0.1.beta.orig/signal.h0100644000076600007650000000073706376753241015456 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp Copyright (C) 1995 Roger Espel Llima signal.h Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef SIGNAL_H #define SIGNAL_H typedef void (*v_i)(int); extern void allsignals(void); #endif utalk-1.0.1.beta.orig/srdp.h0100644000076600007650000000355406376753243015153 0ustar garabikatlas/* SRDP, Semi-Reliable Datagram Protocol Copyright (C) 1995 Roger Espel Llima srdp.h Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef SRDP_H #define SRDP_H #include "srdpdata.h" #include extern int srdp_open(struct srdp_info *info); /* opens the socket, adds it to the global select table, returns 0 if ok, -1 if error */ extern int srdp_select(int width, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); /* does a select on the given fds + on any open sockets, and updates the SRDP_FL_READY flag on any sockets that are ready for reading (which actually means there are data chunks already read) */ extern int srdp_read(struct srdp_info *info, struct srdp_chunk *chunk, int csize); /* reads one data chunk (from the srdp buffers) and returns 1 if ok, or 0 if there was none to read, or 2 if the packet was truncated, -1 if the connection is closed; updates SRDP_FL_READY */ /* the fields sequenced.seq and sequenced|noseq.len get returned in network order! */ extern int srdp_write(struct srdp_info *info, struct srdp_chunk *chunk); /* sends out a data chunk; returns -1 if error, 0 if ok */ /* the field sequenced|noseq.len must be set in network order! */ extern void srdp_synch(struct srdp_info *info); /* sends out an SRDP_CURRENT and SRDP_MISSLST */ extern void srdp_close(struct srdp_info *info, char *message); /* attempts to gracefully close the connection; may block for a few seconds */ extern void srdp_drop(struct srdp_info *info, char *message); /* drops the connection; will not block */ #endif utalk-1.0.1.beta.orig/srdpdata.h0100644000076600007650000001250306376753243015777 0ustar garabikatlas/* SRDP, Semi-Reliable Datagram Protocol Copyright (C) 1995 Roger Espel Llima srdpdata.h Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef SRDPDATA_H #define SRDPDATA_H #include #include #include #include /* Change these if necessary, to match your computer system; the 3 must be unsigned, of respective sizes 32, 16 and 8 bits. */ typedef unsigned int srdp_u32; typedef unsigned short srdp_u16; typedef unsigned char srdp_u8; #define SRDP_VERSION 0x01 #define SRDP_REVISION 0x00 #define SRDP_PING 0xff #define SRDP_PINGREP 0xfd #define SRDP_MISSLST 0xfb #define SRDP_CURRENT 0xf9 #define SRDP_OLDEST 0xf7 #define SRDP_ALIVE 0xf5 #define SRDP_CLOSE 0xfe #define SRDP_DROP 0xfc struct srdp_chunk { union { struct { srdp_u8 version; /* filled in by srdp */ srdp_u8 revision; /* filled in by srdp */ srdp_u8 hi_version; /* to be filled by the application */ srdp_u8 type; /* to be filled by the application */ srdp_u32 len; /* to be filled by the application */ srdp_u32 seq; /* filled in by srdp */ char data; /* to be filled by the application */ } sequenced; struct { srdp_u8 version; srdp_u8 revision; srdp_u8 hi_version; srdp_u8 type; srdp_u32 len; char data; } noseq; } u; }; #define SRDP_HEADER_USEQ_LEN 8 #define SRDP_HEADER_SEQ_LEN 12 struct srdp_chunk_list { struct srdp_chunk_list *prev; struct srdp_chunk_list *next; int lsize; /* to evaluate the size of the list */ struct srdp_chunk *chunk; }; #define SRDP_FL_READY 0x01 /* set by srdp_select() if there's something to read */ #define SRDP_FL_RECVD 0x02 /* set by srdp_select() if we've ever received anything */ #define SRDP_FL_BROKEN 0x04 /* set by srdp_selct() if we haven't received anything for a while */ #define SRDP_FL_NOSELECT 0x08 /* set _by_the_application_ if it doesn't want srdp_select() to return just b/c there's something to srdp_read() here */ #define SRDP_FL_CLOSED 0x10 /* set by srdp_select(), srdp_read() and srdp_drop() and srdp_close() if the connection is closed; the data in the struct info can be reused */ #define SRDP_FL_SELRETURN 0x20 /* set _by_the_application_ if it wants srdp_select() to return even if there wasn't any data for the application, whenever something is read from the socket (the flags may have changed). ignored if SRDP_FL_NOSELECT is set */ #define SRDP_RARRSIZE 64 /* number of u32's in the binary array of received packets; must be a power of 2 */ #define SRDP_RARRMASK (SRDP_RARRSIZE-1) #define SRDP_DEFL_MAX_RCV 32768 #define SRDP_DEFL_MAX_SNT 131072 #define SRDP_DEFL_MAX_PACKET 1472 #define SRDP_CURR_1_TM 4 #define SRDP_CURR_NXT_TM 8 #define SRDP_MISS_TM 5 #define SRDP_OLDEST_TM 5 #define SRDP_ALIVE_TM 10 #define SRDP_BROKEN_TM 30 struct srdp_info { /* these should be filled out before calling srdp_open(), with 0 meaning "any" */ struct in_addr remote; u_short remoteport; /* in host order, 0=any */ u_short localport; /* in host order, 0=any */ /* these should be filled before srdp_open() with either the values or 0 for the recommended defaults */ int max_rcv_lst_size, max_snt_lst_size; /* in bytes */ int max_packet_len; /* in bytes */ int curr_1_time; /* seconds between get packet and send SRDP_CURRENT */ int curr_nxt_time; /* seconds between SRDP_CURRENT and next one */ int miss_time; /* min seconds between two SRDP_MISSLIST */ int oldest_time; /* min seconds between two SRDP_OLDEST */ int alive_time; /* seconds of inactivity before sending SRDP_ALIVE */ int broken_time; /* seconds of not getting anything before raising FL_BROKEN */ int flags; /* the application can set one of two special modes */ /* these will be filled in by srdp_open() */ int sockfd; /* the remaining fields shouldn't be used by the clients */ struct sockaddr_in myaddr; struct srdp_chunk_list *recvlist_first; struct srdp_chunk_list *recvlist_last; struct srdp_chunk_list *sentlist_first; struct srdp_chunk_list *sentlist_last; int recvlist_size; int sentlist_size; srdp_u32 current_seq; /* greatest seq number received so far */ srdp_u32 next_seq; /* next seq number to send */ srdp_u32 *recvdarray; /* bitmapped array of recvd packets, size = power of 2 */ srdp_u32 rarr_seqbase; int rarr_start; /* recvdarray[rarr_start] refers to rarr_seqbase, and the array wraps; less significant bits refer to smaller seq numbers */ int current_sent; /* 1 if we've sent a current since the last recvd packet */ struct timeval lastheard; struct timeval lastsentsomething; struct timeval lastsentcurrent; struct timeval lastsentlist; struct timeval lastsentoldest; struct timeval wakeup1; /* time at which we need to wake up to send a CURRENT */ struct timeval wakeup2; /* time to wake up to send a ALIVE */ }; struct srdp_infolist { struct srdp_info *theinfo; struct srdp_infolist *next; }; #define CHUNKLIST_EXTRA 32 #define MAXPACKETLEN 9000 #endif utalk-1.0.1.beta.orig/struct.h0100644000076600007650000000362506376753244015527 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp by Roger Espel Llima struct.h This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef STRUCT_H #define STRUCT_H #define LINE_CHUNK 40 #define PLINE_CHUNK 500 #define BUFFERS 2 #include "srdpdata.h" typedef void (*ftype)(unsigned char c); struct logical_line { /* the arrays contain always at least 'length' elements */ int number; int length; /* of the logical line */ int arr_length; /* of the arrays */ int any_deleted; /* true if any chars have the special value 0 */ unsigned char *chars; /* entry 0 is unused */ srdp_u32 *seqs; srdp_u32 len_seq; struct logical_line *next, *prev; }; struct physical_line { struct logical_line *line; int offset; /* offset into the lline */ int length; /* length of the pline in non-null chars */ }; struct user { char name[12], hostname[256], tty[16]; srdp_u32 hostaddr; int daemon, ourdaemon; char statuslines[300], lefts[12]; struct srdp_info info; int last_flags, last_active; /* to avoid recalculating statuslines */ struct logical_line *first_lline, *last_lline; struct physical_line *plines; int p_arr_length, nplines; int first_visible_pline; int winsize; int yfirst; /* first y for each window */ int ystatus; /* y for each status line */ }; struct function { char name[40]; ftype func; }; struct mode { ftype funcs[256]; struct binding *bindings; }; struct binding { unsigned char sequence[16]; ftype func; struct binding *next; }; #define EMACS 0 #define VI_CMD 1 #define VI_INS 2 #define N_MODES 3 struct alias { char from[256], to[256]; int type; struct alias *next; }; #define ALIAS_ALL 0 #define ALIAS_BEFORE 1 #define ALIAS_AFTER 2 #endif utalk-1.0.1.beta.orig/termcap.h0100644000076600007650000000302206376753246015627 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp Copyright (C) 1995 Roger Espel Llima termcap.h Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef TERMCAP_H #define TERMCAP_H #define ATTR_BOLD 1 #define ATTR_UNDERLINED 2 #define ATTR_INVERSE 4 extern int tgetent(char *bp, char *name); extern char *tgetstr(char *id, char **area); extern int tgetnum(char *id); extern char *tgoto(char *cap, int col, int row); extern int tputs(char *str, int affcnt, int (*putc)(int)); extern char *t_ce, *t_me, *t_mr, *t_md, *t_us; extern int xcursor, ycursor; /* absolute; -1, -1 = unknown */ extern void putcap(char *s); extern void init_termcap(void); extern void flush_term(void); extern void alloc_termcap_screen(void); extern void clearscreen(void); extern void cleareol(void); extern void beep(void); extern void gotoxy(int x, int y); extern void putscreen(char *s, int n, char attr); extern void redraw(void); extern void redrawlines(int y1, int y2); extern void scrollup(int y1, int y2); extern void scrolldown(int y1, int y2); extern void backspace(void); extern void resize_termcap_screen(int oldlines); #define ATTR_BOLD 1 #define ATTR_UNDERLINED 2 #define ATTR_INVERSE 4 #define setinv() (putcap(t_mr)) #define setunder() (putcap(t_us)) #define setbold() (putcap(t_md)) #define normal() (putcap(t_me)) #endif utalk-1.0.1.beta.orig/termio.h0100644000076600007650000000106706376753247015503 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp Copyright (C) 1995 Roger Espel Llima termio.h Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef TERMIO_H #define TERMIO_H #define SET_RAW 0 #define SET_COOKED 1 extern void get_screensize(void); extern void init_termio(void); extern void settty(int how, int drain); #endif utalk-1.0.1.beta.orig/util.h0100644000076600007650000000113406376753255015153 0ustar garabikatlas/* utalk, a UDP-based "talk" replacement, using srdp Copyright (C) 1995 Roger Espel Llima util.h Started: 19 Oct 95 by This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. */ #ifndef UTIL_H #define UTIL_H extern void barf(char *m); extern void cleanupexit(int n, char *m); extern void *mymalloc(int n); extern void *myrealloc(void *p, int n); extern unsigned char *getword(unsigned char **s); #endif utalk-1.0.1.beta.orig/utalk.help0100644000076600007650000000151606376753253016021 0ustar garabikatlas23 @a@yes, this is the utalk help file! well, it still needs to be written, but you get the idea. btw, you can get to the top with 'a', and you can scroll around with the arrows, space, ^n, ^p, b and hjkl, to suit all styles.. and do not forget, you can get out of here with the magic Enter key, or even with ESC ESC.. isn't life wonderful? isn't utalk even better? @c@yeah, well, i know, this is just some rambling to put here before i take the time to write the actual helpfile.. which i s'pose i'll first wait for the features to be stable, it's such a bother to change documentation afterwards... btw, did i mention that you can get directly to this paragraph with the 'c' key? yeah, i know you're impressed. blah blah blah talk daemons are dumb. i sure wish they'd been a little better designed... @d@isn't this fun? (i know, no) utalk-1.0.1.beta.orig/utalk.10100644000076600007650000003463606376753252015241 0ustar garabikatlas.TH UTALK 1 "Nov 24, 1996" "Roger Espel Llima" .SH NAME utalk \- a UDP-based full screen talk program .SH SYNOPSIS .PD 0 .B utalk [options] user[@host][#tty] .PP .B utalk [options] !port@host .PP .B utalk [options] -s port .PP .B utalk [options] -c host port .PD .SH OPTIONS .TP .BR "-s, --server" Tells .B utalk not to use the talk daemons to announce itself, and instead wait for a connection on the given port number. .TP .BR "-c, --client" Tells .B utalk not to use the talk daemons to announce itself, and instead connect to the given port number on the given host. .TP .BR "-a, --announce-only" Makes .B utalk decide on a local port number, and send out an announce with the port number instead of your username. Useful to start a .B utalk session with a host that has a talk daemon when yours doesn't. .TP .BR "-7, --seven-bit" Makes .B utalk convert all characters to 7-bit US-ASCII before displaying them; useful if you don't have an iso-8859-1 capable terminal. .TP .BR "-8, --eight-bit" Makes .B utalk show iso-8859-1 encoded characters on the screen, as they are received. .SH ARGUMENTS .TP .BR "user@host[#tty]" Specifies the user to ring. The "user@host" part can be replaced with an alias name defined in your .B ~/.utalkrc. If you specify the tty, .B utalk will ask the talk daemon to ring the user on that particular tty. The username can be prefixed by a '!', which .B utalk will strip. .TP .BR "!port@host" Specifies the port number to connect to, and the host. Useful to answer to a .BR "utalk -a" . .SH DESCRIPTION .B utalk is a text-based chat program in the vein of .B talk and .BR YTalk , which uses a better protocol built over UDP for communication. Because it does not require that network packets arrive in sequence to be able to display them, .B utalk can be used over unreliable links where a TCP/IP connection such as a telnet or a talk would be too slow to be usable. .LP Additionally, .B utalk supports full editing of previously typed text, scrollback, keyboard bindings, and aliases. .LP .B utalk is .I incompatible with any other talk programs, as it uses a completely different protocol. Unfortunately, the text "respond with: talk" is hard-coded in the talk daemon, and cannot be set by the client. .LP To avoid confusion, .B utalk's talk requests send the username prefixed with a '!', which must be understood as ``respond with .BR utalk ''. .SH DISPLAY As in .B talk and .BR YTalk , the .B utalk screen is divided in a number of separate scrolling areas, one for yourself and one for each other connected client (currently limited to one). .LP At any time, one of these windows is active. Each scrolling area has a status line at the top, with the name of the client and the following flags: .TP .BR "[m]" This flag is always present on the top window: it's "\fBm\fPy" window. .TP .BR "[*]" This flag means that the window is active and in read-write mode. This is the normal mode for your own window; in this mode, you can type and edit, and when you move your cursor and/or scroll back all other connected clients see the cursor move too. .TP .BR "[R]" This flag means that the window is active and in read-only mode. This is the only way another window than yours can be active (i.e you can't type text in someone else's window); setting your own window in read-only mode lets you scroll back through what you typed without all other clients seeing your do it. .TP .BR "[n]" Means that the window is not yet connected; the user hasn't responded yet. .TP .BR "[c]" Means that the window is connected. .TP .BR "[b] Means that the window is connected, but no data (even control data that .B utalk sends periodically) has been received for a while, so the connection might be broken or the client on the other side might have crashed. .B utalk will not interrupt a connection because of this, it's up to you to stop it after a while if it doesn't come back. .SH EDITING .BR utalk 's editing keys can be configured to emulate either .B vi or .BR emacs , in a limited way. By default, .B utalk is in emacs mode. .LP .B utalk keeps a table of bindings for each of three modes: emacs mode, vi command mode, and vi insert mode. These tables are initialized to suitable defaults, and you can add bindings with the .I bind command in your ~/.utalkrc file. .LP The following is a list of all key commands and their default bindings in emacs mode, vi command mode, and vi insert mode respectively. .TP .BR "self-insert (printable chars) (unbound) (printable chars)" The keypress gets inserted at the current cursor position, and the cursor moves forward one step. .TP .BR "insert-in-place (unbound) (unbound) (unbound)" The keypress gets inserted at the current cursor position, and the cursor does not move. .TP .B "quote-char (^Q) (unbound) (^V)" Insert the following key literally. .TP .BR "tab (^I) (unbound) (^I)" Moves the cursor to the next tab stop. .TP .BR "new-line (^M, ^J) (^M, ^J) (^M, ^J)" Moves the cursor to the first position on the next line, possibly creating the line. .TP .BR "delete (^D) (x) (unbound)" Deletes the character under the cursor. .TP .BR "delete-end-of-line (^K) (D, d$) (unbound)" Deletes from the cursor to the end of the line. .TP .BR "delete-beginning-of-line (unbound) (d0, d^) (unbound)" Deletes from the beginning of the line to the cursor. .TP .BR "delete-line (^U) (dd) (^U)" Deletes the current line. .TP .BR "delete-word (ESC d) (dw, dW) (unbound)" Deletes to the beginning of the next word. .TP .BR "delete-end-of-word (unbound) (de) (unbound)" Deletes to the end of the current word. .TP .B "backspace (^H, DEL) (X) (^H, DEL)" Moves back the cursor one position, erasing the character in that position. .TP .B "backspace-word (^W) (unbound) (^W)" Backspaces over one word. .TP .B "backward (^B, ESC [D) (h, [D) (unbound)" Moves backward one position. .TP .B "forward (^F, ESC [C) (l, [C) (unbound)" Moves forward one position. .TP .B "backward-word (ESC b) (b, B) (unbound)" Moves backward one word. .TP .B "forward-word (ESC f) (w, W) (unbound)" Moves forward one word. .TP .B "end-of-word (unbound) (e) (unbound)" Moves to the end of the current word. .TP .B "beginning-of-line (^A) (0, ^) (unbound)" Moves to the beginning of the current line. .TP .B "end-of-line (^E) ($) (unbound)" Moves to the end of the current line. .TP .B "nop (unbound) (ESC) (unbound)" Does nothing. .TP .B "beep (unbound) (unbound) (unbound)" Beeps the terminal (does not send a beep across to the other clients). .TP .B "up (^P, ESC [A) (k, [A) (unbound)" Moves the cursor up one line. .TP .B "down (^N, ESC [B) (j, [B) (unbound)" Moves the cursor down one line. .TP .B "up-page (ESC v) (^B) (unbound)" Moves the cursor up one page if the screen is in read/write mode, and scrolls up by one page if it is in read-only mode. .TP .B "down-page (^V) (^F) (unbound)" Moves the cursor down one page if the screen is in read/write mode, and scrolls down by one page if it is in read-only mode. .TP .B "up-half-page (unbound) (^U) (unbound)" Moves the cursor up half a page if the screen is in read/write mode, and scrolls up by half a page if it is in read-only mode. .TP .B "down-half-page (unbound) (^D) (unbound)" Moves the cursor down half a page if the screen is in read/write mode, and scrolls down by half a page if it is in read-only mode. .TP .B "top-of-screen (unbound) (H) (unbound)" Moves the cursor to the first line of the current visible screen. .TP .B "middle-of-screen (unbound) (M) (unbound)" Moves the cursor to the middle of the screen. .TP .B "bottom-of-screen (unbound) (L) (unbound)" Moves the cursor to the last line of the current visible screen. .TP .B "top-or-up-page (unbound) (unbound) (unbound)" Moves the cursor to the top of the screen if it's not there, or scrolls up by one page if it is. .TP .B "bottom-or-down-page (unbound) (unbound) (unbound)" Moves the cursor to the bottom of the screen if it's not there, or scrolls down by one page if it is. .TP .B "vi-goto-line (unbound) (G) (unbound)" Moves the cursor to the line number entered as a prefix, if any; otherwise moves to the last line of the buffer. .TP .B "redisplay (^L) (^L) (^L)" Redraws the screen. .TP .B "resynch (^R) (^R) (^R)" Requests immediate transmission of all missing packets. .TP .B "next-window (^X b, ^X o, ^G) (g) (unbound)" Cycles the active window between your window in read/write mode, your window in read-only mode, and each of the other windows. .TP .B "set-topic (^T) (^T) (unbound) Prompts the user for a ``topic'', which will be displayed at the top of the screen for all users. .TP .B "vi-insert-mode (unbound) (i, R) (unbound)" Sets vi insert mode. .TP .B "vi-command-mode (unbound) (unbound) (unbound)" Sets vi command mode. .TP .B "emacs-mode (unbound) (unbound) (unbound)" Sets emacs mode. .TP .B "quit (^X c) (ZZ) (unbound)" Quits .BR utalk . .TP .B "vi-escape (unbound) (unbound) (ESC)" Sets vi command mode and moves the cursor one position to the left. .TP .B "vi-add (unbound) (a) (unbound)" Moves the cursor one position to the right and sets vi insert mode. .TP .B "vi-add-at-end-of-line (unbound) (A) (unbound)" Moves the cursor to the end of the current line and sets vi insert mode. .TP .B "vi-insert-at-beginning-of-line (unbound) (I) (unbound)" Moves the cursor to the beginning of the current line and sets vi insert mode. .TP .B "vi-open (unbound) (o) (unbound)" Moves the cursor to the beginning of the next line and sets vi insert mode. .TP .B "vi-open-above (unbound) (O) (unbound)" Moves the cursor to the beginning of the previous line and sets vi insert mode. .TP .B "vi-replace-char (unbound) (r) (unbound)" Replaces the char under the cursor with the following key. .TP .B "vi-find-char (unbound) (f) (unbound)" Moves the cursor to the next occurrence of the following key on the same line. .TP .B "vi-reverse-find-char (unbound) (F) (unbound)" Moves the cursor to the previous occurrence of the following key on the same line. .TP .B "vi-till-char (unbound) (t) (unbound)" Moves the cursor to one position before the next occurrence of the following key on the same line. .TP .B "vi-reverse-till-char (unbound) (T) (unbound)" Moves the cursor to one position after the previous occurrence of the following key on the same line. .TP .B "vi-repeat-find (unbound) (;) (unbound)" Repeats the previous find or till command. .TP .B "vi-reverse-repeat-find (unbound) (,) (unbound)" Repeats the previous find or till command, reversing the direction. .TP .B "vi-delete-find-char (unbound) (df) (unbound)" Deletes all characters between the current position and the next occurrence of the following key on the same line, both included. .TP .B "vi-delete-reverse-find-char (unbound) (dF) (unbound)" Deletes all characters between the current position and the previous occurrence of the following key on the same line, both included. .TP .B "vi-delete-till-char (unbound) (dt) (unbound)" Deletes all characters between the current position and the next occurrence of the following key on the same line, not including the latter. .TP .B "vi-delete-reverse-till-char (unbound) (dF) (unbound)" Deletes all characters between the current position and the previous occurrence of the following key on the same line, not including the latter. .TP .B "vi-flip-case (unbound) (~) (unbound)" Flip the case of the character under the cursor. .SH CONFIGURATION You can specify a number of settings for .B utalk in a configuration file called .I .utalkrc in your home directory. .LP Valid commands are: .PP .RS 4 .PD 0 emacs-mode .PP vi-mode .PP bind \fIkey\fR \fIfunction\fR .PP bind! \fIkey\fR \fIfunction\fR .PP alias \fIalias\fR \fIvalue\fR .PP set \fIsetting\fR on|off .RE .PD 1 .PP Settings are: .TP .B "beep" Makes utalk let beeps through or silence them. .TP .B "word-wrap, wordwrap, ww" Turns word-wrap on or off (only at the end of the last line in the buffer). .TP .B "eight-bit, eightbit, eb" Lets eight-bit iso-latin-1 characters through or maps them to US-ASCII. .TP .B "meta-esc, metaesc, me" Maps keys with the high bit set to ESC followed by key, or lets them through (only affects emacs-mode). .PD .PP "toggle" and "se" are synonyms for "set". .PD 0 .PP "bindkey" and "bindkey!" are synonyms for "bind" and "bind!", respectively. .PD 1 .LP In settings, "on" and "off" arguments are optional, "on" is assumed by default, unless the setting's name is prefixed with "no". .PP Bindings apply to the current mode; to change bindings in vi mode, put a "vi-mode" first, then your "bind"s and "bind!"s. In emacs mode, "bind" and "bind!" are synonymous. .LP In a binding, the \fIkey\fR must be a character or sequence of characters, not separated with any spaces. The following sequences are recognized to specify characters: .PP .RS 4 .PD 0 ^\fIchar\fR, C-\fIchar\fR -- Control-\fIchar\fR .LP M-\fIchar\fR -- Meta-\fIchar\fR .LP \\e -- ESC .LP \\t -- Tab .LP \\r -- Carriage return (^M) .LP \\n -- Newline (^J) .LP \\x\fIhex code\fR -- Ascii code given in hex .LP \\\fIchar\fR -- That char, taken literally .RE .PD 1 .PP Note that you shouldn't bind functions to M-key combinations in emacs mode; use ESC key combinations instead, and turn meta-esc on if you want to use your Meta key like in emacs. .PD 2 .LP .PD You can make aliases for addresses of people to ring, in either of 3 forms: .TP 12 .B "alias \fIaliasname\fR@ \fIusername\fR@" Replaces \fIaliasname\fR@\fIhost\fR with \fIusername\fR@\fIhost\fR for every host. The '@' at the end of \fIusername@\fR is not required. .TP 12 .B "alias @\fIaliashost\fR @\fIrealhost\fR" Replaces \fIuser\fR@\fIaliashost\fR with \fIuser\fR@\fIrealhost\fR for every user. The '@' at the beginning of @\fIrealhost\fR is not required. .TP 12 .B "alias \fIaliasname\fR \fIuser\fR@\fIhost\fR" Replaces \fIaliasname\fR with \fIuser\fR@\fIhost\fR. .SH COPYING .B utalk is free software. You can redistribute it and/or modify it under the GNU General Public License as published by the Free Software Foundation. See the file LICENSE for details. .SH SEE ALSO .BR talk (1), .BR ytalk (1), .BR mesg (1) .SH FILES ~/.utalkrc configuration file .SH BUGS This is a beta version of .BR utalk , so some bugs are to be expected... please report any bugs to the author. There is no way to insert (rather than overwrite) text. This will be hard to fix, as it is a requirement of .B utalk and the .B SRDP protocol that the whole protocol must be commutative. .LP There is no way (as of yet...) to do n-way .B utalk sessions. .LP The vi and emacs emulations are relatively primitive. .SH AUTHOR .B utalk was written by Roger Espel Llima .