ANNOUNCE000664 045065 024037 00000005450 14144511073 012320 0ustar00fdckermit000000 000000 The Original G-Kermit 1.00 announcement follows. Version 2.00 of 26 May 2021 and 2.01 0f 15 November 2021 are functionally identical but have been adjusted to compile and link successfully on modern Unix platforms such as Ubuntu 20.04, Red Hat EL6, NetBSD 9.2, and macOS 10.15.7 with Xcode 12.4. --------------------------------- From fdc@watsun.cc.columbia.edu Mon Jan 3 17:09:14 EST 2000 Article: 40 of comp.protocols.kermit.announce Path: newsmaster.cc.columbia.edu!watsun.cc.columbia.edu!fdc From: fdc@watsun.cc.columbia.edu (Frank da Cruz) Newsgroups: comp.protocols.kermit.announce Subject: Announcing G-Kermit 1.00 Date: 3 Jan 2000 21:57:59 GMT Organization: Columbia University Lines: 44 Approved: fdc@columbia.edu Message-ID: <84r617$pqb$1@newsmaster.cc.columbia.edu> NNTP-Posting-Host: watsun.cc.columbia.edu X-Trace: newsmaster.cc.columbia.edu 946936679 26443 128.59.39.2 (3 Jan 2000 21:57:59 GMT) X-Complaints-To: postmaster@columbia.edu NNTP-Posting-Date: 3 Jan 2000 21:57:59 GMT Keywords: Kermit G-Kermit Xref: newsmaster.cc.columbia.edu comp.protocols.kermit.announce:40 This to announce a new, compact, and GPL'd Kermit program for UNIX. The new program is called G-Kermit (GNU Kermit). It is intended to meet the need for a Kermit protocol implementation that is: . Stable and low-maintenance . Compact and fast with no frills . Under the GNU Public License G-Kermit is command-line only (no interactive commands or scripting) and remote-mode only (no making connections). It has an extremely simple user interface, and implements a large subset of the Kermit protocol in a small amount of highly portable code. It has been built and tested on a wide variety of UNIX platforms, ranging from early-1980s to up-to-the-minute, using both traditional C and ANSI C. It is designed to be as independent as possible of platform-specific features, and therefore to be stable for many years if we resist the temptation to add features to it. The size of the binary ranges from 33K (on HP-UX 8.00) to 104K on Ultrix/MIPS, with an average size of 53K over 64 builds, and a typical size of 37K on PC-based UNIXes. It's easy to build, install, and uninstall. It requires no privileges. Documentation is included as a plain-text README file and a man page. You can find G-Kermit 1.00 on the Web at: http://www.columbia.edu/kermit/gkermit.html and by FTP at: ftp://kermit.columbia.edu/kermit/archives/gkermit.tar.Z (88K) ftp://kermit.columbia.edu/kermit/archives/gkermit.tar.gz (62K) ftp://kermit.columbia.edu/kermit/bin/gku100.* (individual binaries) Uncompress, untar, read the README file, and take it from there (in most cases you just type "make" to build it). Send test reports to kermit-support@columbia.edu. Frank da Cruz The Kermit Project Columbia University New York City Web: http://www.columbia.edu/kermit/ COPYING000664 045065 024037 00000043105 07026302102 012211 0ustar00fdckermit000000 000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 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. README000664 045065 024037 00000130425 14053725316 012056 0ustar00fdckermit000000 000000 G-Kermit -- GNU Kermit -*- text -*- Version 1.00 : 25 December 1999 Version 2.00 : 26 May 2021 Version 2.00 is identical to 1.00 except that the source code has been tailored to build successfully on modern Unix platforms. Author: Frank da Cruz The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA fdc@columbia.edu (now retired, but the email address still works) CONTENTS... 1. OVERVIEW 2. INVOKING G-KERMIT 3. COMMAND-LINE OPTIONS 4. THE MECHANICS OF FILE TRANSFER 5. INTERRUPTING FILE TRANSFER 6. TEXT AND BINARY TRANSFER MODE 7. PATHNAMES 8. FILENAME CONVERSION 9. FILENAME COLLISIONS 10. KERMIT PROTOCOL DETAILS 11. PROBLEMS, BUGS, ERRORS 12. BUILDING G-KERMIT 13. INSTALLING G-KERMIT 14. DESIGN AND IMPLEMENTATION NOTES 1. OVERVIEW G-Kermit is a Unix program for uploading and downloading files with the Kermit protocol. G-Kermit is a product of Kermit Project at Columbia University. It is free software under the GNU Public License. See the COPYING file for details. This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative. G-Kermit is: . Fast . Small . Portable . Easy to use . Interoperable . Low-maintenance . Stable and reliable Features include: . Text and binary file transfer on both 7-bit and 8-bit connections . Files can be transferred singly or in groups . Automatic startup configuration via GKERMIT environment variable . Configurability as an external protocol Kermit protocol features include: . Automatic peer recognition . Streaming on reliable connections . Selectable packet length, 40 to 9000 bytes (4000 default) . Single shifts for 8-bit data on 7-bit connections . Control-character prefixing for control-character transparency . Control-character unprefixing for increased speed (incoming only) . Compression of repeated bytes . Per-file and batch cancellation Features selectable on command line: . Text or binary mode transfer . Filename conversion on/off . Filename collision backup versus overwrite . Keep or discard incompletely received files . Packet length . Packet timeout . Flow control . Parity . Streaming . Messages . Debugging Features not included (see Section 14): . Making connections . Character-set translation . Interactive commands and scripting . File date-time stamps 2. INVOKING G-KERMIT G-Kermit is always on the "far end" of a connection, on a Unix system that you have made a connection to from a terminal emulator by dialup, network, or direct serial. If you have a direct or dialup serial connection into Unix, use the "stty -a" or "stty all" command to see if your Unix terminal driver is conditioned for the appropriate kind of flow control; if it isn't, very few applications (including gkermit) will work well, or at all. The command for setting terminal flow control varies from platform to platform, but it is usually something like this: $ stty crtscts (where "$ " is the shell prompt) for RTS/CTS hardware flow control, or: $ stty ixon ixoff for Xon/Xoff "software" flow control. When you have a network connection, flow control is usually nothing to worry about, since the network protocol (TCP or X.25) takes care of it automatically, but on certain platforms (such as HP-UX) the TCP/IP Telnet or Rlogin server uses this for flow control between itself and the underlying pseudoterminal in which your session runs, so Xon/Xoff might be required for these sessions too. The G-Kermit binary is called "gkermit". It should be stored someplace in your Unix PATH, such as /usr/local/bin/gkermit or somewhere in the /opt tree on System V R4. To run G-Kermit, just type "gkermit" followed by command- line options that tell it what to do. If no options are given, G-Kermit prints a usage message listing the available options: G-Kermit CU-1.00, Columbia University, 1999-12-25: POSIX. Usage: gkermit [ options ] Options: -r Receive files -s fn Send files -g fn Get files from server -a fn As-name for single file -i Image (binary) mode transfer -T Text mode transfer -P Path/filename conversion disabled -w Write over existing files with same name -K Keep incompletely received files -p x Parity: x = o[dd],e[ven],m[ark],s[pace],n[one] -e n Receive packet-length (40-9000) -b n Timeout (sec, 0 = none) -x Force Xon/Xoff (--x = Don't force Xon/Xoff) -S Disable streaming -X External protocol -q Quiet (suppress messages) -d [fn] Debug to ./debug.log [or specified file] -h Help (this message) More info: http://www.columbia.edu/kermit/ If an option takes an argument, the argument is required; if an option does not take an argument, no argument may be given (exceptions: -d may or may not take an argument; -s can take 1 or more arguments). The action options are -r, -s, and -g. Only one action option may be given. If no action options are given, G-Kermit does nothing (except possibly printing its usage message or creating a debug.log file). Here are some examples (in which "$ " is the shell prompt): $ gkermit -s hello.c <-- Sends the hello.c file. $ gkermit -s hello.* <-- Sends all hello.* files. $ gkermit -r <-- Waits for you to send a file to it. $ gkermit -g hello.c <-- Gets the hello.c file from your computer. $ gkermit -g \*.c <-- Gets all *.c files from your computer. Options that do not take arguments can be "bundled" with other options. An option that takes an argument must always be followed by a space and then its argument(s). Examples: $ gkermit -is hello.o <-- Sends hello.o in binary mode. $ gkermit -dSr <-- Receives with debugging and no streaming. G-Kermit's exit status is 0 if all operations succeeded and 1 if there were any failures. If a group of files was transferred, the exit status is 1 if one or more files was not successfully transferred and 0 if all of them were transferred successfully. 3. COMMAND-LINE OPTIONS -r RECEIVE: This option tells G-Kermit to receive a file or files; that is, to passively wait for you to send files from your terminal emulator. -s fn SEND: This tells G-Kermit to send the file or files specified by fn, which can be a filename, a regular expression, or a list of filenames and/or regular expressions (wildcards). Regular expressions are interpreted and expanded by your shell into the list of names of files that is given to G-Kermit. For example "*.c" expands to a list of all files in the current directory whose names end with ".c". -g fn GET: This option tells G-Kermit to get a file (or files) from a Kermit server. It is useful only when your terminal emulator supports the Kermit autodownload feature AND it includes a Kermit server mode. It is equivalent to "gkermit -r", escaping back, telling your terminal emulator to send the given files, and then reconnecting to Unix. -a fn AS-NAME: When used with -s, this option tells G-Kermit to send the file whose name is given as the first -s argument under the name fn. For example, "gkermit -s game -a work" sends the file called "game" under the name "work", so the receiver will think its name is "work". When given with the -r or -g command, the incoming file (or the first incoming file if there is more than one) is stored under the name fn. In all cases, the given name is used as-is; it is not converted. -i IMAGE (binary) mode transfer. When used with -s, tells G-Kermit to send in binary mode. When used with -r, tells G-Kermit to receive in binary mode if the file sender does not specify the transfer mode (text or binary). When used with -g, tells G-Kermit to ask your terminal emulator's Kermit to send the given file in binary mode. See Section 6 for details. -T TEXT mode transfer (note uppercase T). When used with -s, tells G-Kermit to send in text mode. When used with -r, tells G-Kermit to receive in text mode if the file sender does not specify the transfer mode (text or binary). When used with -g, tells G-Kermit to ask your emulator's Kermit to send the given file in text mode. See Section 6 for details. -P PATH (filename) conversion disabled (note uppercase P). Normally when sending files, G-Kermit converts filenames to a form that should be acceptable to non-Unix platforms, primarily changing lowercase letters to uppercase, ensuring there is no more than one period, and replacing any "funny" characters by X or underscore (explained in Section 8). -w WRITEOVER. When receiving, and an incoming file has the same name as an existing file, write over the existing file. By default G-Kermit backs up the existing file by adding a suffix to its name (see Section 9). -K KEEP incompletely received files. Normally when receiving files, and a file transfer is interrupted, G-Kermit discards the partially received file so you won't think you have the whole file. Include -K on the command line to tell G-Kermit to keep partially received files, e.g. "gkermit -Kr". -p x PARITY: Use the given kind of parity, where x can be 'n' for None (which is the default, for use on 8-bit-clean connections); 's' for Space, 'e' for Even, 'o' for Odd, and 'm' for Mark. 's' might be needed on certain Telnet connections; 'e', 'o', and 'm' are only for serial connections; don't try them on TCP/IP connections. -e n PACKET LENGTH: Receive packet-length, where n can be any number between 40 and 9000. The default length on most platforms is 4000. Use this option to specify a different length; usually this would be necessary only if transfers fail using the default length due to some kind of buffering problem in the host or along the communication path. Example: "gkermit -e 240 -r". -b n TIMEOUT (sec, 0 = none). Specify the number of seconds to wait for a packet before timing out and retransmitting. By default, G-Kermit uses whatever timeout interval your terminal emulator's Kermit asks it to use. No need to change this unless the timeout action causes problems. -x XON/XOFF. Force Xon/Xoff flow control in the Unix terminal driver. Try this if uploads fail without it. But don't use it if you don't need to; on some platforms or connections it hurts rather than helps. --x Don't force Xon/Xoff; for use when G-Kermit was built with the SETXONXOFF compile-time option (Section 12), to override the automatic setting of Xon/Xoff in case it interferes with file transfers. -S STREAMING disabled. Streaming is a high-performance option to be used on reliable connections, such as in Telnet or Rlogin sessions. It is used if your terminal emulator's Kermit requests it. Use the -S option (note: uppercase S) to suppress this feature in case it causes trouble. Details in Section 10. -X EXTERNAL PROTOCOL. Include this option when invoking G-Kermit from another program that redirects G-Kermit's standard i/o, e.g. over a connection to another computer. If you omit this switch when using G-Kermit as an external protocol to another communications program, G-Kermit is likely to perform illegal operations and exit prematurely. If you include this switch when G-Kermit is NOT an external protocol to another program, file transfers will fail. G-Kermit has no way of determining automatically whether it is being used as an external protocol. -q QUIET. Suppresses messages. -d DEBUG. Use this for troubleshooting. It creates a file called debug.log in your current directory, to be used in conjunction with the source code, or sent to the Kermit support address for analysis. More about this in Section 11. -d fn DEBUG to specified file (rather than default ./debug.log). -h HELP: Displays the usage message shown above. You may supply options to G-Kermit on the command line or through the GKERMIT environment variable, which can contain any valid gkermit command-line options. These are processed before the actual command-line options and so can be overridden by them. Example for bash or ksh, which you can put in your profile if you want to always keep incomplete files, suppress streaming, suppress messages, and use Space parity: export GKERMIT="-K -S -q -p s" G-Kermit's options are compatible with C-Kermit's, with the following exceptions: -P (available only in C-Kermit 7.0 and later) -K (currently not used in C-Kermit) -b (used in C-Kermit for serial device speed) -S (used in C-Kermit to force an interactive command prompt) -x (used in C-Kermit to start server mode) --x (currently not used in C-Kermit) -X (currently not used in C-Kermit) 4. THE MECHANICS OF FILE TRANSFER To transfer files with G-Kermit you must be connected through a terminal emulator to the Unix system where G-Kermit is installed, meaning you are online to Unix and have access to the shell prompt (or to some menu that has an option to invoke G-Kermit), and your terminal emulator must support the Kermit file transfer protocol. The connection can be serial (direct or dialed) or network (Telnet, Rlogin, X.25, etc). 4.1. Sending Files When you tell G-Kermit to SEND a file (or files), e.g. with: $ gkermit -Ts oofa.txt it pauses for a second and then sends its first packet. What happens next depends on the capabilities of your terminal emulator: . If your emulator supports Kermit "autodownloads" then it receives the file automatically and puts you back in the terminal screen when done. . Otherwise, you'll need to take whatever action is required by your emulator to get its attention: a mouse action, a keystroke like Alt-x, or a character sequence like Ctrl-\ or Ctrl-] followed by the letter "c" (this is called "escaping back") and then tell it to receive the file. When the transfer is complete, you might have to instruct your emulator to go back to its terminal screen. During file transfer, most terminal emulators put up some kind of running display of the file transfer progress. 4.2. Receiving Files When you tell G-Kermit to RECEIVE, this requires you to escape back to your terminal emulator and instruct it to send the desired file(s). Autodownload is not effective in this case. When the transfer is complete, you'll need to instruct your emulator to return to its terminal screen. 4.3. Getting Files If your terminal emulator supports Kermit autodownloads AND server mode, you can use GET ("gkermit -g files...") rather than RECEIVE ("gkermit -r"), and the rest happens automatically, as when G-Kermit is sending. 5. INTERRUPTING FILE TRANSFER G-Kermit supports file and group interruption. The method for interrupting a transfer depends on your terminal emulator. For example, while the file-transfer display is active, you might type the letter 'x' to cancel the current file and go on to the next one (if any), and the letter 'z' to cancel the group. Or there might be buttons you can click with your mouse. When G-Kermit is in packet mode and your terminal emulator is in its terminal screen, you can also type three (3) Ctrl-C characters in a row to make G-Kermit exit and restore the normal terminal modes. 6. TEXT AND BINARY TRANSFER MODE When sending files in binary mode, G-Kermit sends every byte exactly as it appears in the file. This mode is appropriate for program binaries, graphics files, tar archives, compressed files, etc, and is G-Kermit's default file-transfer mode when sending. When receiving files in binary mode, G-Kermit simply copies each byte to disk. (Obviously the bytes are encoded for transmission, but the encoding and decoding procedures give a replica of the original file after transfer.) When sending files in text mode, G-Kermit converts the record format to the common one that is defined for the Kermit protocol, namely lines terminated by carriage return and linefeed (CRLF); the receiver converts the CRLFs to whatever line-end or record-format convention is used on its platform. When receiving files in text mode, G-Kermit simply strips carriage returns, leaving only a linefeed at the end of each line, which is the Unix convention. When receiving files, the sender's transfer mode (text or binary) predominates if the sender gives this information to G-Kermit in a Kermit File Attribute packet, which of course depends on whether your terminal emulator's Kermit protocol has this feature. Otherwise, if you gave a -i or -T option on the gkermit command line, the corresponding mode is used; otherwise the default mode (binary) is used. Furthermore, when either sending or receiving, G-Kermit and your terminal emulator's Kermit can inform each other of their OS type (Unix in G-Kermit's case). If your emulator supports this capability, which is called "automatic peer recognition", and it tells G-Kermit that its platform is also Unix, G-Kermit and the emulator's Kermit automatically switch into binary mode, since no record-format conversion is necessary in this case. Automatic peer recognition is disabled automatically if you include the -i (image) or -T (text) option. When sending, G-Kermit sends all files in the same mode, text or binary. There is no automatic per-file mode switching. When receiving, however, per-file switching occurs automatically based on the incoming Attribute packets, if any (explained below), that accompany each file, so if the file sender switches types between files, G-Kermit follows along. 7. PATHNAMES When SENDING a file, G-Kermit obtains the filenames from the command line. It depends on the shell to expand metacharacters (wildcards and tilde). G-Kermit uses the full pathname given to find and open the file, but then strips the pathname before sending the name to the receiver. For example: $ gkermit -s /etc/hosts results in an arriving file called "HOSTS" or "hosts" (the directory part, "/etc/", is stripped; see next section about capitalization). However, if a pathname is included in the -a option, the directory part is not stripped: $ gkermit -s /etc/hosts -a /tmp/hosts This example sends the /etc/hosts file but tells the receiver that its name is "/tmp/hosts". What the receiver does with the pathname is, of course, up to the receiver, which might have various options for dealing with incoming pathnames. When RECEIVING a file, G-Kermit does NOT strip the pathname, since incoming files normally do not include a pathname unless you told your terminal to include them or gave an "as-name" including a path when sending to G-Kermit. If the incoming filename includes a path, G-Kermit tries to store the file in the specified place. If the path does not exist, the transfer fails. The incoming filename can, of course, be superseded with the -a option. 8. FILENAME CONVERSION When sending a file, G-Kermit normally converts outbound filenames to common form: uppercase, no more than one period, and no funny characters. So, for example, gkermit.tar.gz would be sent as GKERMIT_TAR.GZ. When receiving a file, if the name is all uppercase, G-Kermit converts it to all lowercase. If the name contains any lowercase letters, G-Kermit leaves the name alone. Otherwise G-Kermit accepts filename characters as they are, since Unix allows filenames to contain practically any characters. If the automatic peer recognition feature is available in the terminal emulator, and G-Kermit recognizes the emulator's platform as Unix, G-Kermit automatically disables filename conversion and sends and accepts filenames literally. You can force literal filenames by including the -P option on the command line. 9. FILENAME COLLISIONS When G-Kermit receives a file whose name is the same as that of an existing file, G-Kermit backs up the existing file by adding a unique suffix to its name. The suffix is ".~n~", where n is a number between 1 and 999. This the same kind of backup suffix used by GNU EMACS and C-Kermit (both of which can be used to prune excess backup files). But since G-Kermit does not read directories (see Implementation Notes below), it can not guarantee that the number chosen will be higher than any other backup prefix number for the same file. In fact, the first free number, starting from 1, is chosen. If an incoming file already has a backup suffix, G-Kermit strips it before adding a new one, rather than creating a file that has two backup suffixes. To defeat the backup feature and have incoming files overwrite existing files of the same name, include the -w (writeover) option on the command line. If G-Kermit has not been been given the -w option and it fails to create a backup file, the transfer fails. 10. KERMIT PROTOCOL DETAILS Block check G-Kermit uses the 3-byte, 16-bit CRC by default. If the other Kermit does not agree, both Kermits automatically drop down to the single-byte 6-bit checksum that is required of all Kermit implementations. Attributes When sending files, G-Kermit conveys the file transfer mode and file size in bytes to the receiver in an Attribute (A) packet if the use of A-packets was negotiated. This allows the receiver to switch to the appropriate mode automatically, and to display the percent done, estimated time left, and/or a thermometer bar if it has that capability. When receiving, G-Kermit looks in the incoming A-packet, if any, for the transfer mode (text or binary) and switches itself accordingly on a per-file basis. Handling of the Eighth Bit G-Kermit normally treats the 8th bit of each byte as a normal data bit. But if you have a 7-bit connection, transfers of 8-bit files fail unless you tell one or both Kermits to use the appropriate kind of parity, in which case Kermit uses single-shift escaping for 8-bit bytes. Generally, telling either Kermit is sufficient; it tells the other. Use the -p option to tell G-Kermit which parity to use. Locking shifts are not included in G-Kermit. Control-Character Encoding G-Kermit escapes all control characters when sending (for example, Ctrl-A becomes #A). When receiving, it accepts both escaped and bare control characters, including NUL (0). However, unescaped control characters always present a danger, so if uploads to G-Kermit fail, tell your terminal emulator's Kermit to escape most or all control characters (in C-Kermit and Kermit 95 the command is SET PREFIXING CAUTIOUS or SET PREFIXING ALL). Packet Length All legal packet lengths, 40-9020, are supported although a lower maximum might be imposed on platforms where it is known that bigger ones don't work. When receiving, G-Kermit sends its receive packet length to the sender, and the sender must not send packets any longer than this length. The default length for most platforms is 4000 and it may be overridden with the -e command-line option. Sliding Windows G-Kermit does not support sliding windows. Streaming is used instead. If the other Kermit bids to use sliding windows, G-Kermit declines. Streaming If the terminal emulator's Kermit informs G-Kermit that it has a reliable connection (such as TCP/IP or X.25), and the emulator's Kermit supports streaming, then a special form of the Kermit protocol is used in which data packets are not acknowledged; this allows the sender to transmit a steady stream of (framed and checksummed) data to the receiver without waiting for acknowledgements, allowing the fastest possible transfers. Streaming overcomes such obstacles as long round trip delays, unnecessary retransmissions on slow network connections, and most especially the TCP/IP Nagle and Delayed ACK heuristics which are deadly to a higher-level ACK/NAK protocol. When streaming is in use on a particular connection, Kermit speeds are comparable to FTP. The drawback of streaming is that transmission errors are fatal; that's why streaming is only used on reliable connections, which, by definition, guarantee there will be no transmission errors. However, watch out for the relatively rare circumstance in which emulator thinks it has a reliable connection when it doesn't -- for example a Telnet connection to a terminal server, and a dialout from the terminal server to the host. Use the -S option on the command line to defeat streaming in such situations. Using all defaults on a TCP/IP connection on 10BaseT (10Mbps) Ethernet from a modern Kermit program like C-Kermit 7.0 or Kermit 95, typical transfer rates are 150-500Kcps. 11. PROBLEMS, BUGS, ERRORS If file transfers fail: . Make sure your terminal emulator is not unprefixing control characters; various control characters might cause trouble along the communication path. When in doubt, instruct the file sender to prefix all control characters. . Make sure your Unix terminal is conditioned for the appropriate kind of flow control. . Use command-line options to back off on performance and transparency; use -S to disable streaming, -e to select a shorter packet length, -p to select space or other parity, -b to increase or disable the timeout, and/or establish the corresponding settings on your emulator. When receiving files in text mode, G-Kermit strips all carriage returns, even if they aren't part of a CRLF pair. If you have a TCP/IP connection (e.g. Telnet or Rlogin) to Unix from a terminal emulator whose Kermit protocol does not support streaming, downloads from G-Kermit are likely to be as much as 10 or even 100 times slower than uploads if the TCP/IP stack engages in Nagle or Delayed ACK heuristics; typically, when your terminal emulator's Kermit protocol sends an acknowledgment, the TCP stack holds on to it for (say) 1/5 second before sending it, because it is "too small" to send right away. As noted in Section 9, the backup prefix is not guaranteed to be the highest number. For example, if you have files oofa.txt, oofa.txt.~1~, and oofa.txt.~3~ in your directory, and a new oofa.txt file arrives, the old oofa.txt is backed up to oofa.txt.~2~, rather than oofa.txt.~4~ as you might expect. This is because gkermit lacks directory reading capabilities, for reasons noted in Section 14, and without this, finding the highest existing backup number for a file is impractical. If you send a file to G-Kermit with streaming active when the connection is not truly reliable, all bets are off. A fatal error should occur promptly, but if huge amounts of data are lost, G-Kermit might never recognize a single data packet and therefore not diagnose a single error; yet your terminal emulator keeps sending packets since no acknowledgments are expected; the transfer eventually hangs at the end of file. Use -S on G-Kermit's command line to disable streaming in situations where the terminal emulator requests it in error. You can use G-Kermit's debug log for troubleshooting; this is useful mainly in conjunction with the source code. But even if you aren't a C programmer, it should reveal any problem in enough detail to help pinpoint the cause of the failure. "gkermit -d" (with no action options) writes a short debug.log file that shows the build options and settings. The debug log is also a packet log; to extract the packets from it, use: grep ^PKT debug.log Packets in the log are truncated to avoid wrap-around on your screen, and they have the Ctrl-A packet-start converted to ^ and A to avoid triggering a spurious autodownload when displaying the log on your screen. In certain circumstances it is not desirable or possible to use -d to create a log file called debug.log in the current directory; for example, if you don't have write access to the current directory, or you already have a debug.log file that you want to keep (or transfer). In this case, you can include a filename argument after -d: gkermit -d /tmp/testing.log -s *.c (This is an exception to the rule that option arguments are not optional.) If all else fails, you can contact the Kermit Project for technical support; see: http://www.columbia.edu/kermit/support for instructions. 12. BUILDING G-KERMIT G-Kermit is written to require the absolute bare minimum in system services and C-language features and libraries, and therefore should be portable to practically any Unix platform at all with any C compiler. The source files are: makefile The build procedure gwart.c Source code for a mini-lex substitute gproto.w G-Kermit protocol state machine to be preprocessed by gwart gkermit.h G-Kermit header file gkermit.c G-Kermit main module and routines gcmdline.c G-Kermit command-line parser gunixio.c Unix-specific i/o routines A simple makefile is provided, which can be used with make or gmake. There are three main targets in the makefile: posix Build for any POSIX.1 compliant platform (termios). This is the default target, used if you type "make" (or "gmake") alone. This target works for most modern Unixes, including GNU/Linux, FreeBSD, OpenBSD, NetBSD, BSDI, HP-UX, Solaris, SunOS, Unixware, AIX, etc. sysv Build for almost any AT&T System V platform (termio). Examples include AT&T Unix releases, e.g. for the AT&T 7300, HP-UX versions prior to 7.00. bsd Build for any BSD (pre-4.4) or Unix V7 platform (sgtty). Examples include NeXTSTEP 3.x, OSF/1, and 4.3BSD or earlier. Note that the target names are all lowercase; "posix" is the default target (the one used if you just type "make"). If the build fails with a message like: gunixio.c: 65: Can't find include file termios.h *** Error code 1 then try "make sysv" or "make bsd". See the build list below for examples. Some special build targets are also provided: sysvx Like sysv but uses getchar()/putchar() for packet i/o rather than buffered nonblocking read()/write(); this is necessary for certain very old System V platforms (see description of USE_GETCHAR below). stty When none of the other targets compiles successfully, try this one, which runs the external stty program rather than trying to use API calls to get/set terminal modes (system("stty raw -echo") and system("stty -raw echo")). Several maintenance/management targets are also included: clean Remove object and intermediate files. install Install gkermit (read the makefile before using this). uninstall Uninstall gkermit from wherever "make install" installed it. The default compiler is cc. To override (e.g. to force the use of gcc on computers that have both cc and gcc installed, or that don't have cc), use: [g]make CC=gcc [] No other tools beyond make, the C compiler and linker, a short list of invariant header files, and the standard C library are needed or used. The resulting binary should be 100K or less on all hardware platforms (and 64K or less on most; see list below). You may also specify certain build options by including a KFLAGS clause on the make command line, e.g.: make "KFLAGS=-DSETXONXOFF -DEXTRADEBUG" sysv By default, nonblocking buffered read() is used for packets; this technique works on most platforms but other options -- USE_GETCHAR and DUMBIO -- are provided when it doesn't work or when nonblocking i/o is not available. The build options include: __STDC__ Include this when the compiler requires ANSI prototyping but does does not define __STDC__ itself. Conversely, you might need to include -U__STDC__ if the compiler defines __STDC__ but does not support minimum ANSI features. ULONG=long Include this if compilation fails with "unknown type: unsigned long". CHAR=char Include this if compilation fails with "unknown type: unsigned char". SMALL Define this when building on or for a "small" platform, for example a 16-bit architecture. USE_GETCHAR Specifies that packet i/o should be done with (buffered) getchar() and putchar() rather than the default method of nonblocking, internally buffered read() and write(). Use this only when G-Kermit does not build or run otherwise, since if the default i/o code is not used, G-Kermit won't be able to do streaming. DUMBIO Specifies that packet i/o should be done with blocking single-byte read() and write(). Use this only when G-Kermit doesn't build or run, even with USE_GETCHAR. MAXRP=nnn Change the maximum receive-packet length to something other than the default, which is 9020. You should change this only to make it smaller; making it bigger is not supported by the Kermit protocol. DEFRP=nnn Change the default receive packet length to something other than the default, which is 4000. Making it any bigger than this is not advised. TINBUFSIZ=nnn On builds that use nonblocking buffered read(), override the default input buffer size of 4080. SETXONXOFF On some platforms, mainly those based on System V R4 and earlier, it was found that receiving files was impossible on TCP/IP connections unless the terminal driver was told to use Xon/Xoff flow control. If downloads work but uploads consistently fail (or fail consistently whenever streaming is used or the packet length is greater than a certain number like 100, or 775), try adding this option. When gkermit is built with this option, it is equivalent to the user always giving the -x option on the command line. (Most versions of HP-UX need this; it is defined automatically at compile time if __hpux is defined.) SIG_V The data type of signal handlers is void. This is set automatically for System V and POSIX builds. SIG_I The data type of signal handlers is int. This is set automatically for BSD builds. NOGETENV Add this to disable the feature in which G-Kermit gets options from the GKERMIT environment variable. NOSTREAMING Add this to disable streaming. EXTRADEBUG This adds a lot (a LOT) of extra information to the debug log regarding packet and character-level i/o. FULLPACKETS Show full packets in the debug log rather than truncating them. Any compiler warnings should be harmless. Examples include: "Passing arg 2 of `signal' from incompatible pointer" (or "Argument incompatible with prototype"): Because no two Unix platforms agree about signal handlers. Harmless because the signal handler does not return a value that is used. We don't want to open the door to platform-specific #ifdefs just to silence this warning. However, you can include -DSIG_I or -DSIG_V on the CC command line to override the default definitions. " declared but never used": Some function parameters are not used because they are just placeholders or compatibility items, or even required by prototypes in system headers. Others might be declared in system header files (like mknod, lstat, etc, which are not used by G-Kermit). "Do you mean equality?": No, in "while (c = *s++)" the assignment really is intentional. "Condition is always true": Yes, "while (1)" is always true. "Flow between cases": Intentional. "No flow into statement": In gproto.c, because it is a case statement generated by machine, not written by a human. The coding conventions are aimed at maximum portability. For example: . Only relatively short identifiers. . No long character-string constants. . Only #ifdef, #else, #endif, #define, and #undef preprocessor directives. . Any code that uses ANSI features is enclosed in #ifdef __STDC__..#endif. . No gmake-specific constructs in the makefile. Here are some sample builds (December 1999): Platform Size Target Notes Apple Mac OS X 1.0 gcc: 48K posix (AKA Rhapsody 5.5) AT&T 3B2/300 SVR2 cc: 49K sysv (4) AT&T 6300 PLUS cc: 58K sysv (6) AT&T 7300 UNIX PC cc: 40K sysv AT&T 7300 UNIX PC gcc: 55K sysv (23K with shared lib) BSDI 4.0.1 gcc: 34K posix DEC 5000 MIPS Ultrix 4.3 cc: 99K posix DEC Alpha Digital UNIX 3.2 cc: 98K bsd (AKA OSF/1) (1) DEC Alpha Tru64 UNIX 4.0e cc: 82K bsd (1) DEC PDP-11 2.11BSD cc: 40K bsd211 (7) DG/UX 5.4R3.10 cc: 52K posix DG/UX 5.4R4.11 gcc: 51K posix DYNIX/ptx 4.4.2 cc: 43K posix FreeBSD 2.2.7 gcc: 41K posix FreeBSD 3.3 gcc: 34K posix GNU/Linux RH 5.2 gcc: 35K posix (RH = Red Hat) GNU/Linux RH 6.1 gcc: 44K posix GNU/Linux SW 3.5 gcc: 34K posix (SW = Slackware) GNU/Linux SW 4.0 gcc: 36K posix GNU/Linux SW 7.0 gcc: 44K posix HP-UX 5.21 cc: 55K sysv (2) HP-UX 6.5 cc: 40K sysv (5) HP-UX 7.05 cc: 50K posix HP-UX 8.00 gcc: 33K posix HP-UX 9.05 cc: 57K posix HP-UX 10.01 cc: 57K posix HP-UX 10.20 cc: 61K posix IBM AIX 3.2 IBM cc: 62K posix IBM AIX 4.1.1 IBM cc: 67K posix IBM AIX 4.3.2 IBM cc: 69K posix Motorola 88K SV/88 R4.3 42K posix Motorola 68K SV/68 R3.6 56K sysv (4) NetBSD 1.4.1 gcc: 41K posix NeXTSTEP m68k 3.1 gcc: 77K bsd (3) NeXTSTEP m68k 3.3 gcc: 78K bsd (3) OpenBSD 2.5 gcc: 47K posix QNX 4.25 cc: 33K posix SCO XENIX 2.3.4 cc: 41K sysv (4) SCO UNIX 3.2v4.2 cc: 73K posix SCO UNIX 3.2v4.2 gcc: 61K posix SCO ODT 3.0 cc: 97K posix SCO OSR5.0.5 gcc: 42K posix SCO Unixware 2.1.3 cc: 38K posix SCO Unixware 7.0.1 cc: 37K posix SGI IRIX 5.3 cc: 86K posix SGI IRIX 6.5.4 cc: 91K posix SINIX 5.42 MIPS cc: 57K posix Solaris 2.4 cc: 50K posix Solaris 2.5.1 cc: 51K posix Solaris 2.6 cc: 52K posix Solaris 7 cc: 52K posix SunOS 4.1.3 cc: 57K posix SunOS 4.1.3 gcc: 64K posix Notes: (1) "make posix" builds without complaint on OSF/1 (Digital UNIX (Tru64)) but it doesn't work -- i/o hangs or program dumps core. "make bsd" works fine. (2) POSIX APIs not available in this antique OS (circa 1983). Also due to limited terminal input buffering capacity, streaming must be disabled and relatively short packets must be used when receiving: "gkermit -Se 250 -r". However, it can use streaming when sending. (3) POSIX APIs not available. (4) On System V R3 and earlier, EWOULDBLOCK is not defined, so we use EGAIN instead. No special build procedures needed. (5) Built with 'make -i "KFLAGS=-DDEFRP=512 -DUSE_GETCHAR" sysv'. It can be built without -DUSE_GETCHAR but doesn't work. (6) Use 'make "CC=cc -Ml" "KFLAGS=-DUSE_GETCHAR sysv'. It builds but doesn't work, reason unknown, but probably because it was never designed to be accessed remotely in the first place. (7) This is a 16-bit architecture. A special makefile target is needed because its make program does not expand the $(CC) value when invoking second-level makes. Packet and buffer sizes are reduced to keep static data within limits. Overlays are not needed. 13. INSTALLING G-KERMIT The makefile creates a binary called "gkermit". Simply move this binary to the desired directory, such as /usr/local/bin. It needs no special permissions other than read, write, and execute for the desired users and groups: no setuid, no setgid, or any other form of privilege. It should be called "gkermit" and not "kermit", since "kermit" is the binary name for C-Kermit, and the two are likely to be installed side by side on the same computer; even when they are not, consistent naming is better for support and sanity purposes. There is also a short man page: gkermit.nr You can view it with: nroff -man gkermit.nr | more Rename and store it appropriately. In addition, this file itself (README) should be made available in a public documentation directory as: gkermit.txt The makefile includes a SAMPLE 'install' target that does all this. Please read it before use to be sure the appropriate directories and permissions are indicated. There is also an 'uninstall' target to undo an installation. Obviously you need write access to the relevant directories before you can install or uninstall G-Kermit. 14. DESIGN AND IMPLEMENTATION NOTES A primary objective in developing G-Kermit is that it can be released and used forever without constant updates to account for platform idiosyncracies and changes. For this reason, certain features have been deliberately omitted: . File timestamps. The methods for dealing with internal time formats are notoriously unportable and also a moving target, especially now with the 32-bit internal time format rollover looming in 2038 and the time_t data type changing out from under us. Furthermore, by excluding any date-handling code, G-Kermit is automatically Y2K, 2038, and Y10K compliant. . Internal wildcard expansion, recursive directory traversal, etc. Even after more than 30 years, there is still no standard and portable service in Unix for this. . Hardware flow control, millisecond sleeps, nondestructive input buffer peeking, threads, select(), file permissions, etc etc. Other features are omitted to keep the program small and simple, and to avoid creeping featurism: . Sliding windows. This technique is more complicated than streaming but not as fast, and furthermore would increase the program size by a factor of 5 or 10 due to buffering requirements. . An interactive command parser and scripting language (because users always want more and more commands and features). . Character set conversion (because users always want more and more character sets). Adding character set support would increase the program size by a factor of 2 to 4, depending on the selection of sets. . Making connections (because this requires huge amounts of tricky and unstable high-maintenance platform- and device-specific code for serial ports, modems, modem signals, network stacks, etc). All of the above can be found in C-Kermit, which is therefore bigger and more complicated, with more platform-specific code and #ifdef spaghetti. C-Kermit requires constant updates and patches to keep pace with changes in the underlying platforms, networking methods, and demands from its users for more features. The goal for G-Kermit, on the other hand, is simplicity and stability, so we don't need thousands of #ifdefs like we have in C-Kermit, and we don't need to tweak the code every time a new release of each Unix variety comes out. G-Kermit is meant to be PORTABLE and LONG-LASTING so the stress is on a MINIMUM of platform dependencies. If you make changes, please try to avoid adding anything platform-dependent or in any other way destabilizing. Bear in mind that the result of your changes should still build and run successfully on at least all the platforms where it was built originally. In any case, you are encouraged to send any changes back to the Kermit Project to be considered for addition to the master G-Kermit distribution. 15. FURTHER INFORMATION The Kermit protocol is specified in "Kermit, A File Transfer Protocol" by Frank da Cruz, Digital Press (1987). A correctness proof of the Kermit protocol appears in "Specification and Validation Methods", edited by Egon Boerger, Oxford University Press (1995). "Using C-Kermit" by Frank da Cruz and Christine M. Gianone, Digital Press (1997, or later edition) explains many of the terms and techniques referenced in this document in case you are not familiar with them, and also includes tutorials on data communications, extensive troubleshooting and performance tips, etc. Various other books on Kermit are available from Digital Press. Online resources include: http://www.columbia.edu/kermit/ The Kermit Project website comp.protocols.kermit.misc The unmoderated Kermit newsgroup kermit-support@columbia.edu Technical support Also visit: http://www.columbia.edu/kermit/gkermit.html for a more up-to-date version of this file, complete with download links. (End of G-Kermit README) gcmdline.c000664 045065 024037 00000024130 14053715441 013115 0ustar00fdckermit000000 000000 /* G C M D L I N -- gkermit command line parser */ /* Author: Frank da Cruz Originally at: The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA http://www.columbia.edu/kermit/ kermit@columbia.edu As of October 2011: The New Open-Source Kermit Project Bronx NY http://kermitproject.org kermit@kermitproject.org Copyright (C) 1999, 2021, The Trustees of Columbia University in the City of New York. Issued under the GNU General Public License as it existed in 1999. 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. */ #include #include "gkermit.h" /* Externals */ extern int nfils, parity, text, backup, rpsiz, urpsiz, timint; extern int literal, quiet, keep, streamok, nomodes, manual, xonxoff, noxonxoff; extern char ttname[], *cmerrp, *cmarg, *cmarg2; extern FILE * db; /* Variables exported from this module */ extern char **cmlist; /* Pointer to file list in argv */ extern char **xargv; /* Global copies of argv */ extern int xargc; /* and argc */ /* Variables and symbols local to this module */ static int action = 0; /* Action selected on command line */ _MYPROTOTYPE( static int doarg, (char) ); _MYPROTOTYPE( VOID fatal, (char *) ); _MYPROTOTYPE( VOID usage, (void) ); #ifndef NOGETENV /* getenv prototype commented out in G-Kermit 2.0 */ /* _MYPROTOTYPE( char * getenv, (char *) ); */ #define GARGC 32 #define GBUFSIZ 256 static char gbuf[GBUFSIZ], *gargs[GARGC], *gptr = NULL; static int gargc; #endif /* NOGETENV */ int /* Command-line parser */ cmdlin() { char c; int x; #ifndef NOGETENV char * p = NULL; #endif /* NOGETENV */ cmarg = ""; /* Initialize results */ cmlist = NULL; action = 0; #ifndef NOGETENV if ((p = getenv("GKERMIT"))) { int i, xc, flag = 0; char **xv = NULL; strncpy(gbuf,p,GBUFSIZ-1); /* Make a pokeable copy */ gbuf[GBUFSIZ-1] = NUL; gptr = p; p = gbuf; /* Turn it into an argument vector */ for (i = 0; gbuf[i] && i < GBUFSIZ && gargc < GARGC; i++) { if (!flag) { if (gbuf[i] <= SP) continue; flag = 1; gargs[gargc++] = &gbuf[i]; } else if (flag && gbuf[i] <= SP) { gbuf[i] = NUL; flag = 0; continue; } } xv = xargv; /* Save original argument vector */ xc = xargc; xargv = gargs; /* Redirect it to the one we made */ xargc = gargc; while (xargc-- > 0) { /* Go through the words */ if (**xargv == '-') { /* Got an option (begins with dash) */ c = *(*xargv+1); /* Get the option letter */ x = doarg(c); /* Go handle the option */ if (x < 0) doexit(1); } else { /* No dash where expected */ fprintf(stderr, "?GKERMIT variable option error: \"%s\"\n", *xargv ); usage(); /* Give usage message */ doexit(0); } xargv++; } xargv = xv; /* Restore original argument vector */ xargc = xc; } #endif /* NOGETENV */ while (--xargc > 0) { /* Go through command line words */ xargv++; if (**xargv == '-') { /* Got an option (begins with dash) */ c = *(*xargv+1); /* Get the option letter */ x = doarg(c); /* Go handle the option */ if (x < 0) doexit(1); } else { /* No dash where expected */ fprintf(stderr,"?Command-line option error: \"%s\"\n", *xargv); usage(); /* Give usage message */ doexit(1); } } return(action); /* Then do any requested protocol */ } /* D O A R G -- Do a command-line argument. */ static int #ifdef __STDC__ doarg(char x) #else doarg(x) char x; #endif /* __STDC__ */ { int z; char *xp, **p; xp = *xargv+1; /* Pointer for bundled args */ while (x) { switch (x) { case 'r': /* Receive */ if (action) fatal("conflicting actions"); action = 'v'; break; case 's': /* Send */ if (action) fatal("conflicting actions"); if (*(xp+1)) fatal("invalid argument bundling after -s"); nfils = 0; /* Initialize file counter, flag */ cmlist = xargv+1; /* Remember this pointer */ if (zchki(*cmlist) < 0) fatal("file not found or not accessible"); while (--xargc > 0) { /* Traverse the list */ *xargv++; if (**xargv == '-') break; nfils++; } xargc++, *xargv--; /* Adjust argv/argc */ if (nfils < 1) fatal("missing filename for -s"); action = 's'; break; case 'g': /* get */ if (action) fatal("conflicting actions"); if (*(xp+1)) fatal("invalid argument bundling after -g"); *xargv++, xargc--; if ((xargc == 0) || (**xargv == '-')) fatal("missing filename for -g"); cmarg = *xargv; action = 'r'; break; case 'h': /* Help */ case '?': usage(); doexit(0); case 'i': /* Binary (image) file transfer */ manual = 1; text = 0; break; case 'T': /* Text file transfer */ manual = 1; text = 1; break; case 'p': /* Parity */ if (*(xp+1)) fatal("invalid argument bundling"); *xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) fatal("missing parity"); switch(x = **xargv) { case 'e': /* Even */ case 'o': /* Odd */ case 'm': /* Mark */ case 's': parity = x; break; /* Space */ case 'n': parity = 0; break; /* None */ default: fatal("invalid parity"); } break; case 'w': /* Writeover */ backup = 0; /* Don't back up existing files */ break; case 'd': /* Debug */ p = xargv; *p++; if ((xargc < 2) || (**p == '-')) { db = fopen("debug.log","w"); } else { *xargv++, xargc--; db = fopen(*xargv,"w"); } if (db) { extern char * versio, * build; if (!versio) versio = ""; if (!build) build = ""; debug = 1; setbuf(db,NULL); fprintf(db,"%s: %s\n", (*versio ? versio : "GKERMIT VERSION UNKNOWN"), (*build ? build : "BUILD UNKNOWN") ); fprintf(db,"MAXPATHLEN = %d\n",MAXPATHLEN); if (gptr) fprintf(db,"GKERMIT=\"%s\"\n",gptr); } break; case 'a': /* As-name */ if (*(xp+1)) fatal("invalid argument bundling after -a"); *xargv++, xargc--; if ((xargc == 0) || (**xargv == '-')) fatal("missing name for -a"); cmarg2 = *xargv; if (debug) fprintf(db,"as-name: %s\n",cmarg2); break; case 'e': if (*(xp+1)) fatal("invalid argument bundling after -e"); xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) fatal("missing length"); z = atoi(*xargv); /* Convert to number. */ if (z >= 40 && z <= MAXRP) { rpsiz = urpsiz = z; if (z > 94) rpsiz = 94; /* Fallback if other Kermit can't */ } else { /* do long packets. */ fatal("Unsupported packet length"); } break; case 'b': /* Timeout */ if (*(xp+1)) fatal("invalid argument bundling after -b"); xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) fatal("missing value"); z = atoi(*xargv); /* Convert to number */ if (z < 0) z = 0; if (z > 90) z = 90; timint = z; break; case 'P': /* Path (file) names literal */ literal = 1; break; case 'q': /* Quiet */ quiet = 1; break; case 'K': /* Keep incompletely received files */ keep = 1; break; case 'S': /* Disable streaming */ streamok = -1; break; case 'X': /* gkermit is an external protocol */ quiet = 1; /* No messages */ nomodes = 1; /* Don't set tty modes */ break; case 'x': /* Force Xon/Xoff */ xonxoff = 1; noxonxoff = 0; break; case '-': /* Don't force Xon/Xoff */ if (*(xp+1) == 'x') { xonxoff = 0; noxonxoff = 1; } else { fatal("invalid argument, type 'gkermit -h' for help"); } xp++; break; default: /* Anything else */ fatal("invalid argument, type 'gkermit -h' for help"); } x = *++xp; /* See if options are bundled */ } if (debug) { if (action) fprintf(db,"cmdlin action = %c\n",action); else fprintf(db,"cmdlin action = (none)\n"); } return(action); } VOID fatal(msg) char *msg; { /* Fatal error message */ fprintf(stderr,"\r\nFatal: %s",msg); /* doexit supplies crlf.. */ doexit(1); /* Exit indicating failure */ } VOID usage() { extern char * versio, * build, * url, * email; if (!versio) versio = ""; if (!*versio) versio = "gkermit UNKNOWN VERSION"; if (!build) build = "UNKNOWN BUILD"; if (!url) url = ""; if (!email) email = ""; fprintf(stderr,"%s: %s.\n",versio,build); fprintf(stderr,"Usage: gkermit [ options ]\n"); fprintf(stderr,"Options:\n"); fprintf(stderr," -r Receive files\n"); fprintf(stderr," -s fn Send files\n"); fprintf(stderr," -g fn Get files from server\n"); fprintf(stderr," -a fn As-name for single file\n"); fprintf(stderr," -i Image (binary) mode transfer\n"); fprintf(stderr," -T Text mode transfer\n"); fprintf(stderr," -P Path/filename conversion disabled\n"); fprintf(stderr," -w Write over existing files with same name\n"); fprintf(stderr," -K Keep incompletely received files\n"); fprintf(stderr," -p x Parity: x = o[dd],e[ven],m[ark],s[pace],n[one]\n") ; fprintf(stderr," -e n Receive packet-length (40-%d)\n",MAXRP); fprintf(stderr," -b n Timeout (sec, 0 = none)\n"); fprintf(stderr," -x Force Xon/Xoff (--x = Don't force Xon/Xoff)\n"); fprintf(stderr," -S Disable streaming\n"); fprintf(stderr," -X External protocol\n"); fprintf(stderr," -q Quiet (suppress messages)\n"); fprintf(stderr," -d [fn] Debug to ./debug.log [or specified file]\n"); fprintf(stderr," -h Help (this message)\n"); if (*url) fprintf(stderr, "Web: %s\n",url); if (*email) fprintf(stderr,"Email: %s\n",email); } gkermit.c000664 045065 024037 00000106666 14144472452 013017 0ustar00fdckermit000000 000000 /* G K E R M I T -- GNU Kermit */ /* Sat Dec 25 19:22:54 1999: A free implementation of the Kermit file transfer protocol for UNIX. If you change this software, please either (a) send changes back to the Kermit Project to be considered for the base version; or (b) change the strings below to show a new version number and date and to reflect the person or organization responsible for the new version. ................................. Wed May 26 08:47:48 2021: In the years following the initial rlease of this program, which was designed to be eternal, the C libraries and header files changed and changed and changed to the extent that in 2021 G-Kermit 1.0 ccan't be built at all an most Unix platforms, and even on those where a functional executable is generated, dozens or hundreds of warnings are issued. This, for code that generated zero warnings or errors when compiled in 1999. Thus version 2.0 of May 26, 2021. The only changes are: 1. The version identification just below, and: 2. The inclusion of four previously unneeded header files in gkermit.h. ................................. Mon Nov 15 08:31:48 2021: From Kenji Rikitake, two patches added to gkermit.h for macOS with Xcode: 1. macOS with Xcode has no 2. macOS with Xcode has the prototype of sleep() in Conditional if(n)def with __APPLE__ flag, dedicated for macOS. It seems unistd.h is now required on many platforms; I now #include it in all builds by default; to suppress the #include on platforms that don't have unistd.h, do: make "KFLAGS=-DNOUNISTD" ... */ /* char *versio = "G-Kermit CU-1.00, Columbia University, 1999-12-25"; char *versio = "G-Kermit 2.00, The Kermit Project, 2021-05-26"; */ char *versio = "G-Kermit 2.01, The Kermit Project, 2021-11-15"; char *url = "http://www.kermitproject.org/gkermit.html"; char *email = "kermit@kermitproject.edu"; /* Author: Frank da Cruz Originally at: The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA http://www.columbia.edu/kermit/ kermit@columbia.edu As of October 2011: The New Open-Source Kermit Project Bronx NY http://kermitproject.org kermit@kermitproject.org Copyright (C) 1999, 2021 The Trustees of Columbia University in the City of New York. Issued under the GNU General Public License as it existed in 1999. 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. */ #ifdef POSIX char *build = "POSIX"; /* Identify which build */ #else #ifdef SYSV char *build = "SYSV"; #else #ifdef BSD char *build = "BSD"; #else char *build = "stty"; #endif /* BSD */ #endif /* SYSV */ #endif /* POSIX */ #define _GKERMIT_C #include #include "gkermit.h" /* Forward declarations of functions used within this module... */ _MYPROTOTYPE( int cmdlin, (void) ); /* Command-line parser */ _MYPROTOTYPE( int spacket, (char,int,int,char *) ); /* Make & send a packet */ _MYPROTOTYPE( int rpacket, (void) ); /* Read a packet */ _MYPROTOTYPE( unsigned int chk1, (char *,int) ); /* 1-byte block check */ _MYPROTOTYPE( unsigned int chk3, (char *,int) ); /* 3-byte CRC */ _MYPROTOTYPE( unsigned int chksum, (char *,int) ); /* Checksum */ _MYPROTOTYPE( int getpkt, (int) ); /* Fill packet data field */ _MYPROTOTYPE( VOID encode, (int,int) ); /* Encode a character */ _MYPROTOTYPE( VOID decstr, (char *) ); /* Decode a memory string */ /* Externs */ extern int zincnt; /* For buffered file i/o */ extern char * zinptr; extern FILE * ofp; /* Output file pointer */ /* Global variables declared here */ char *cmarg, *cmarg2 = NULL; /* Send filename pointers */ char **cmlist = NULL; /* Pointer to file list in argv */ FILE * db = NULL; /* Debug log file pointer */ int debug = 0; /* Debugging on */ int failure = 0; /* Return status */ int retries = 0; /* Packet retry counter */ int sendtype = 0; /* Type of last packet sent */ int recvtype = 0; /* Type of last packet received */ int datalen = 0; /* Length of received data field */ /* Protocol parameters */ int spsiz = 90, /* Default send-packet size */ rpsiz = 94, /* Default short receive-packet size */ urpsiz = DEFRP, /* Default long receive-packet size */ maxrp = MAXRP, /* Maximum long receive-packet size */ timint = 9, /* Timeout interval I use */ rtimo = 7, /* Timeout I want you to use */ rpadn = 0, /* How much padding to send */ spadn = 0, /* How much padding to ask for */ bctr = 3, /* Block check type requested */ bctu = 1, /* Block check type used */ ebq = '&', /* 8th bit prefix */ ebqflg = 0, /* 8th-bit quoting flag */ rqf = -1, /* Flag used in 8bq negotiation */ rq = 0, /* Received 8bq bid */ sq = 'Y', /* Sent 8bq bid */ rpt = 0, /* Repeat count */ rptq = '~', /* Repeat prefix */ rptflg = 0, /* Repeat processing flag */ backup = 1, /* Back up existing files */ seq = 0, /* Current packet number */ size, /* Current size of output pkt data */ osize, /* Previous output packet data size */ maxsiz, /* Max size for building data field */ rln, /* Received packet length */ rsn, /* Received packet sequence number */ sndpkl; /* Length of packet being sent */ char spadc = 0, /* Padding character to send */ rpadc = 0, /* Padding character to ask for */ seol = CR, /* End-Of-Line character to send */ reol = CR, /* End-Of-Line character to look for */ rctlq = '#', /* Control prefix in incoming data */ sctlq = '#', /* Outbound control character prefix */ sndpkt[MAXSP+16], /* Entire packet being sent */ rcvpkt[MAXRP+16], /* Packet most recently received */ *rdatap, /* Pointer to data field of rcvpkt */ xxdata[MAXRP+16], /* Packet data buffer */ *isp = NUL, /* Input string pointer */ *osp = NUL, /* Output string pointer */ smark = '\1', /* Outbound packet-start character */ rmark = '\1'; /* Incoming packet-start character */ char * xdata = xxdata; /* Packet data field pointer */ /* File-related variables */ char filnam[MAXPATHLEN+1]; /* Name of current file. */ char ttname[DEVNAMLEN+1]; /* Name of communication device. */ int nfils; /* Number of files to send */ int cx = 0, cz = 0; /* File and batch interruption flags */ int quiet = 0; /* Suppress messages */ int keep = 0; /* Keep incompletely received files */ int streamok = 0; /* Streaming protocol negotiated */ int streaming = 0; /* Streaming active now */ int nomodes = 0; /* Don't mess with tty modes */ int xonxoff = 0; /* Force Xon/Xoff */ int noxonxoff = 0; /* Don't force Xon/Xoff */ int manual = 0; /* Transfer mode manual, not auto */ int parity = 0; /* Parity specified, 0,'e','o',etc */ int delay = 1; /* Initial delay before sending */ int text = 0; /* Flag for text/binary mode */ int first = 0; /* Flag for first input from file */ int longpackets = 0; /* Long packets negotiated */ int attributes = 0; /* Attribute packets negotiated */ int literal = 0; /* Literal filenames */ char start = 0; /* Starting state for automaton */ char **xargv; /* Global copies of argv */ int xargc; /* and argc */ char *dftty = CTTNAM; /* Default controlling terminal name */ char * #ifdef __STDC__ mystrchr(char * s, char c) #else mystrchr(s,c) char *s, c; #endif /* __STDC__ */ { if (!s) /* strchr() replacement */ return(NULL); /* because strchr not portable */ while (*s && *s != c) s++; return((*s == c) ? s : NULL); } int /* Main Program */ main(argc,argv) int argc; char **argv; { xargc = argc; /* Make global copies of argc */ xargv = argv; /* ...and argv. */ start = 0; /* No default start state. */ seq = 0; /* Packet sequence number. */ parity = 0; /* Initial parity. */ strncpy(ttname,dftty,DEVNAMLEN); /* Default device name. */ if (argc < 2) { /* If no args */ usage(); /* give usage message */ doexit(1); /* and quit. */ } sysinit(); /* Set up interrupt handlers, etc */ if (urpsiz > MAXRP) /* In case MAXRP < DEFRP... */ urpsiz = MAXRP; if (maxrp > 9020) maxrp = 9020; start = cmdlin(); /* Parse args */ if (start == 0 && !debug) { /* Nothing to do? */ fprintf(stderr,"No start state\n"); doexit(1); } if (ttopen(ttname) < 0) { /* "Open" the communication device */ fprintf(stderr,"Can't open terminal\n"); doexit(1); } if (ttpkt(parity) < 0) { /* Open OK, put it in packet mode */ printf("Can't set packet mode\n"); doexit(1); } /* OK, do requested actions. */ if (start) { /* Start state */ if (start == 's' || start == 'r') /* If sending or getting*/ ttflui(); /* flush comm line input buffer */ gwart(); /* Do the protocol */ } doexit(failure); /* Done, exit. */ return(failure); /* To shut up picky compilers. */ } /* Functions */ int /* Called implicitly by protocol */ input() { /* to get the next packet. */ int x, type; if (start != 0) { /* Start state in effect? */ type = start; /* Yes, call it a packet type, */ start = 0; /* nullify the start state, */ if (debug) fprintf(db,"input[%c]\n",(char)type); return(type); /* and return the type. */ } if (streaming && (sendtype == 'D')) { /* If streaming and sending data */ x = ttchk(); /* Look for return traffic */ if (debug) fprintf(db,"input streaming ttchk %d\n",x); if (x < 0) errpkt("Fatal i/o error"); else if (x < 5) return('Y'); /* If none, pretend we got an ACK */ timint = 4; type = rpacket(); /* Something came in, read it */ timint = 0; if (mystrchr("TQN",type)) errpkt("Transmission error while streaming"); } else { type = rpacket(); /* No start state, read a packet. */ while (rsn != seq || /* Sequence number doesn't match, or */ mystrchr("TQN",type) /* Timeout, Checksum error, or NAK */ ) { if (streaming) errpkt("Transmission error while streaming"); resend(); /* Resend previous packet. */ type = rpacket(); /* Try to read response. */ } } if (!streaming) /* Got a good packet */ ttflui(); /* Flush buffer if not streaming */ if (debug) fprintf(db,"input[%c]\n",(char)type); return(type); /* Return its type */ } VOID nxtpkt() { /* Next packet */ retries = 0; /* Reset per-packet retry count */ seq = (seq + 1) & 63; /* Next packet number, mod 64 */ } int ack() { /* Acknowledge */ int x = 0; /* Empty acknowledgement */ if (!streaming || recvtype != 'D') x = spacket('Y',seq,0,""); /* Send the packet */ nxtpkt(); /* Increment packet number */ return(x); } int ack1(s) char *s; { /* Acknowledgement with data */ int x; x = spacket('Y',seq,strlen(s),s); /* Send the packet */ nxtpkt(); /* Increment packet number */ return(x); } int nak() { /* Negative acknowledgement */ int x; retries++; if (debug) fprintf(db,"nak #%d (%d/%d)\n",seq,retries,MAXTRY); if (retries > MAXTRY) { /* Check retry limit */ errpkt("Too many retries"); doexit(1); } x = spacket('N',seq,0,""); /* A NAK never has data */ return(x); } VOID tinit() { /* Transaction Initialization */ osp = NULL; /* Reset output string pointer */ cx = cz = 0; /* File interruption flags off */ bctu = 1; /* Block check back to 1 */ ebqflg = rptflg = 0; /* 8th bit & repeat quoting off */ sq = 'Y'; /* Normal 8th bit quote bid */ rqf = -1; /* Flag that no 8-b-q bid seen yet */ seq = 0; /* Start off with packet zero */ *filnam = *sndpkt = *rcvpkt = NUL; /* Clear out string buffers */ } VOID errpkt(s) char *s; { /* Fatal error */ if (start == 'v' || start == 'r') /* Receiving a file? */ sleep(1); /* Time to soak up incoming junk. */ spacket('E',seq,(int)strlen(s),s); /* Send Error packet. */ doexit(1); /* Exit with failure status. */ } int #ifdef __STDC__ sinit(char c) #else sinit(c) char c; #endif /* __STDC__ */ { /* Begin send */ char *s; s = rpar(); /* Get our protocol parameters */ if (c == 'S') { /* Sending a file... */ tmsgl(versio); tmsgl("Escape back to your local Kermit and give a RECEIVE command."); tmsgl(""); tmsgl("KERMIT READY TO SEND..."); sleep(delay); } return(spacket(c,seq,strlen(s),s)); /* Send S or I packet */ } int sfile() { /* Send file header packet */ int x; char pktnam[MAXPATHLEN]; if (debug) fprintf(db,"sfile filnam [%s] max = %d\n",filnam,MAXPATHLEN); if (zopeni(filnam) < 0) { /* Open the input file */ if (debug) fprintf(db,"sfile error %d",errno); errpkt("Failure to open file"); } if (cmarg2) /* User-supplied name - use as-is */ strncpy(pktnam,cmarg2,MAXPATHLEN); else /* Actual filename - convert */ zltor(filnam,pktnam,MAXPATHLEN); cmarg2 = NULL; /* Only works for one file */ if (debug) fprintf(db,"sfile pktnam %s\n",pktnam); x = encstr(pktnam); /* Encode name */ if (debug) fprintf(db,"sfile encstr[%s] x=%d\n",xdata,x); first = 1; /* Starting condition for file */ maxsiz = spsiz - (bctr + 3); /* Maximum packet data field length */ if (spsiz > 94) maxsiz -= 4; /* Long packet has more fields */ nxtpkt(); /* Get next packet number */ return(spacket('F',seq,x,xdata)); /* Send filename */ } int sattr() { /* Build and send A packet */ int i, aln; int binary; extern long filelength; nxtpkt(); /* Get next packet sequence number */ if (debug) fprintf(db,"sattr seq %d\n",seq); i = 0; /* i = Data field character number */ binary = !text; /* (for notational convenience) */ xdata[i++] = '"'; if (binary) { /* Binary */ xdata[i++] = tochar(2); /* Two characters */ xdata[i++] = 'B'; /* B for Binary */ xdata[i++] = '8'; /* 8-bit bytes (note assumption...) */ } else { /* Text */ xdata[i++] = tochar(3); /* Three characters */ xdata[i++] = 'A'; /* A = (extended) ASCII with CRLFs */ xdata[i++] = 'M'; /* M for carriage return */ xdata[i++] = 'J'; /* J for linefeed */ xdata[i++] = '*'; /* Encoding */ xdata[i++] = tochar(1); /* Length of value is 1 */ xdata[i++] = 'A'; /* A for ASCII */ } if (filelength > -1L) { xdata[i] = '1'; /* File length in bytes */ sprintf((char *) &xdata[i+2],"%ld",filelength); aln = (int)strlen((char *)(xdata+i+2)); xdata[i+1] = tochar(aln); i += aln + 2; } xdata[i] = NUL; /* Terminate last good field */ aln = (int)strlen((char *)xdata); /* Get overall length of attributes */ if (debug) fprintf(db,"sattr data %s\n",xdata); return(spacket('A',seq,aln,xdata)); /* Send it */ } #define FTBUFL 10 /* File type buffer */ int gattr(s) char *s; { /* Get attributes from other Kermit */ char c; int aln, i; static char ftbuf[FTBUFL+1]; int retcode; /* Return code */ retcode = 0; /* Initialize return code. */ while ((c = *s++)) { /* Get attribute tag */ aln = xunchar(*s++); /* Length of attribute string */ switch (c) { case '"': /* File type (text, binary, ...) */ for (i = 0; (i < aln) && (i < FTBUFL); i++) ftbuf[i] = *s++; /* Copy it into a static string */ ftbuf[i] = '\0'; if (i < aln) s += (aln - i); if ((*ftbuf != 'A' && *ftbuf != 'B' && *ftbuf != 'I')) { retcode = -1; /* Reject the file */ break; } if (*ftbuf == 'A') { /* Check received attributes. */ text = 1; /* Set current type to Text. */ } else if (*ftbuf == 'B') { text = 0; } break; default: /* Unknown attribute */ s += aln; /* Just skip past it */ break; } } return(retcode); } int sdata() { /* Send a data packet */ int x; if (cx || cz) { /* Interrupted... */ if (debug) fprintf(db,"sdata interrupted cx = %d cz = %d\n",cx,cz); failure = 1; return(0); } x = getpkt(maxsiz); /* Fill data field from input file */ if (x < 1) { if (debug) fprintf(db,"sdata getpkt = %d\n",x); return(0); } nxtpkt(); /* Get next packet number */ return(spacket('D',seq,x,xdata)); /* Send the packet */ } int seof(s) char *s; { /* Send end-of-file packet */ if (debug) fprintf(db,"seof [%s]\n",s); if (zclosi() < 0) { /* Close the file */ if (debug) fprintf(db,"seof zclosi failure %d\n",errno); errpkt("Failure to close input file"); } else { int x; nxtpkt(); /* Get next packet number */ x = spacket('Z',seq,strlen(s),s); /* Send EOF packet */ if (debug) fprintf(db,"seof spacket %d\n",x); return(x); } return(0); /* To shut up picky compilers */ } int seot() { /* Send end-of-transmission packet */ nxtpkt(); return(spacket('B',seq,0,"")); } int closof() { /* Close output file */ if (zcloso(cx|cz) < 0) errpkt("Failure to close output file"); return(0); } int /* Frame and send a packet */ #ifdef __STDC__ spacket(char type,int n,int len,char *d) #else spacket(type,n,len,d) char type, *d; int n, len; #endif /* __STDC__ */ { register int i = 0; int j, k, m, x; char * p = sndpkt; for (i = 0; i < spadn; i++) /* Do requested padding */ p[i] = spadc; p[i++] = smark; /* Send-packet mark */ k = i++; /* Remember this place */ p[i++] = tochar(n); /* Sequence number */ p[i++] = type; /* Packet type */ j = len + bctu; if ((len + bctu + 2) > 94) { /* If long packet */ x = j / 95; /* Use long-packet format */ p[k] = tochar(0); p[i++] = tochar(x); x = j % 95; p[i++] = tochar(x); p[i] = NUL; /* Terminate for header checksum */ p[i++] = tochar(chk1(&p[k],5)); /* Insert header checksum */ } else /* Short packet */ p[k] = tochar(j+2); for (j = len; j > 0; j--) { /* Data */ p[i++] = *d++; } p[i] = NUL; /* Null-terminate */ m = i - k; switch (bctu) { /* Block Check Type Used? */ case 1: /* Type 1 - 6 bit checksum */ p[i++] = tochar(chk1(p+k,m)); break; case 2: /* Type 2 - 12 bit checksum */ j = chksum(p+1,m); p[i++] = tochar((j >> 6) & 077); p[i++] = tochar(j & 077); break; case 3: /* Type 3 - 16 bit CRC-CCITT */ j = chk3(p+1,m); p[i++] = tochar((j >> 12) & 017); p[i++] = tochar((j >> 6) & 077); p[i++] = tochar(j & 077); break; } p[i++] = seol; /* End of line */ p[i] = NUL; /* Null string-terminator */ sndpkl = i; /* Remember length. */ x = ttol(p,sndpkl); /* Send the packet. */ if (x > -1) sendtype = type; /* Remember its type */ return(x); } int resend() { /* Resend a packet */ int x; if (*sndpkt) { retries++; if (debug) fprintf(db,"resend #%d (%d/%d)\n",seq,retries,MAXTRY); if (retries > MAXTRY) { /* Check retry limit */ errpkt("Too many retries"); doexit(1); } x = ttol(sndpkt,sndpkl); /* Send previous packet */ } else { x = nak(); /* or NAK if nothing sent yet. */ } return(x); } int rpacket() { /* Read a packet */ register int i = 0; int j, x, type, rlnpos, rpktl; /* Local variables */ unsigned int c1, c2; char * p = rcvpkt; char pbc[5]; /* Packet block check */ rsn = rln = -1; /* In case of failure. */ *p = NUL; /* Initialize the receive buffer. */ j = ttinl(p,maxrp,timint,reol,rmark,0); /* Read a raw packet */ if (j < 5) { /* Error */ if (j == -1) return('T'); /* Timed out. */ if (j < -1) doexit(1); /* I/O error, hangup, etc. */ if (debug) fprintf(db,"rpacket runt"); return('Q'); /* Runt */ } rpktl = j; /* Remember length. */ rlnpos = ++i; /* Length position. */ if ((j = xunchar(p[i++])) == 0) { /* Get packet length. */ if ((j = rlnpos+5) > rpktl) { /* Long packet */ if (debug) fprintf(db,"rpacket bad length"); return('Q'); /* Too long */ } x = p[j]; /* Header checksum. */ p[j] = NUL; /* Calculate & compare. */ if (xunchar(x) != chk1(p+rlnpos,5)) { if (debug) fprintf(db,"rpacket header checksum: %u\n",x); return('Q'); } p[j] = x; /* Checksum ok, put it back. */ rln = xunchar(p[j-2]) * 95 + xunchar(p[j-1]) - bctu; j = 3; /* Data offset. */ } else if (j < 3) { /* Bad length */ if (debug) fprintf(db,"rpacket bad xlength"); return('Q'); } else { /* Regular packet */ rln = j - bctu - 2; /* No extended header */ j = 0; } rsn = xunchar(p[i++]); /* Sequence number */ type = p[i++]; /* Packet type */ if (debug) fprintf(db,"rpacket type=%c, seq=%02d, len=%d\n",type,rsn,rln); i += j; rdatap = p+i; /* The data itself */ j = rln + i; /* Position of block check */ for (x = 0; x < bctu; x++) /* Copy the block check. */ pbc[x] = p[j+x]; pbc[x] = NUL; p[j] = NUL; /* Null-terminate the data */ datalen = j - i; /* Remember data length */ switch (bctu) { /* Check the block check */ case 1: c1 = (unsigned)xunchar((unsigned)((unsigned)(*pbc) & 0xff)); c2 = chk1(p+rlnpos,j-rlnpos); if (c1 != c2) { if (debug) fprintf(db,"rpacket chk1 (0x%x:0x%x)\n",c1,c2); return('Q'); } break; case 2: c1 = (unsigned)xunchar(*pbc) << 6 | (unsigned)xunchar(pbc[1]); c2 = chksum(p+rlnpos,j-rlnpos); if (c1 != c2) { if (debug) fprintf(db,"rpacket chk2 (0x%x:0x%x)\n",c1,c2); return('Q'); } break; case 3: c1 = ((unsigned)(xunchar((unsigned)((unsigned)*pbc & 0xff)) << 12) | (unsigned)(xunchar((unsigned)((unsigned)pbc[1] & 0xff)) << 6) | (unsigned)(xunchar((unsigned)((unsigned)pbc[2] & 0xff)))) & 0xffff; c2 = chk3(p+rlnpos,j-rlnpos); if (c1 != c2) { if (debug) fprintf(db,"rpacket crc (0x%x:0x%x)\n",c1,c2); return('Q'); } break; default: errpkt("BLOCKCHECK"); } recvtype = type; /* Remember type. */ return(type); /* Good packet, return type. */ } unsigned int chksum(p,len) char *p; int len; { /* Generate arithmetic checksum */ register unsigned int k, m; m = (parity) ? 0177 : 0377; for (k = 0; len-- > 0; *p++) k += (unsigned)*p & m; return(k); } unsigned int chk1(packet,len) char *packet; int len; { /* Generate Type 1 block check */ unsigned int x; x = chksum(packet,len); if (debug) fprintf(db,"chksum=%u\n",x); return((((x & 192) >> 6) + x) & 63); } unsigned int chk3(s,len) char *s; int len; { /* Tableless 16-bit CRC generator */ register unsigned int c; register ULONG q, crc = 0L; while (len-- > 0) { c = (unsigned)(*s++); if (parity) c &= 0177; q = (crc ^ c) & 017; crc = (crc >> 4) ^ (q * 010201); q = (crc ^ (c >> 4)) & 017; crc = (crc >> 4) ^ (q * 010201); } if (debug) fprintf(db,"crc=%u\n",(unsigned int)(crc & 0xffff)); return((unsigned int)(crc & 0xffff)); } int getpkt(maxlen) int maxlen; { /* Fill a packet from file */ int i, next; static int c; static char remain[6] = { NUL, NUL, NUL, NUL, NUL, NUL }; if (first == 1) { /* If first time thru... */ first = 0; /* don't do this next time, */ *remain = NUL; /* discard any old leftovers. */ if (isp) { /* Get first byte. */ c = *isp++; /* Of memory string... */ if (!c) c = -1; } else { /* or file... */ c = zgetc(text); } if (c < 0) { /* Watch out for empty file. */ first = -1; return(size = 0); } } else if (first == -1 && !*remain) { /* EOF from last time? */ return(size = 0); } for (size = 0; (xdata[size] = remain[size]) != NUL; size++) ; *remain = NUL; if (first == -1) return(size); rpt = 0; /* Initialize repeat counter. */ while (first > -1) { /* Until end of file or string... */ if (isp) { next = *isp++; if (!next) next = -1; } else { next = zgetc(text); } if (next < 0) first = -1; /* If none, we're at EOF. */ osize = size; /* Remember current size. */ encode(c,next); /* Encode the character. */ c = next; /* Old next char is now current. */ if (size == maxlen) return(size); /* If just at end, done. */ if (size > maxlen) { /* Past end, must save some. */ for (i = 0; (remain[i] = xdata[osize+i]) != NUL; i++) ; size = osize; xdata[size] = NUL; return(size); /* Return size. */ } } return(size); /* EOF, return size. */ } int encstr(s) char *s; { /* Fill a packet from string s. */ first = 1; /* Start lookahead. */ isp = s; /* Set input string pointer */ getpkt(spsiz); /* Fill a packet */ isp = NULL; /* Reset input string pointer */ return(size); /* Return data field length */ } VOID decstr(s) char *s; { /* Decode packet data into a string */ osp = s; /* Set output string pointer */ decode(datalen); /* Decode the string */ *osp = NUL; /* Terminate with null */ osp = NULL; /* Reset output string pointer */ } VOID encode(a,next) int a, next; { /* Encode character a into packet */ int a7, b8; if (rptflg) { /* Doing run-length encoding? */ if (a == next) { /* Yes, got a run? */ if (++rpt < 94) { /* Yes, count. */ return; } else if (rpt == 94) { /* If at maximum */ xdata[size++] = rptq; /* Emit prefix, */ xdata[size++] = tochar(rpt); /* and count, */ rpt = 0; /* and reset counter. */ } } else if (rpt == 1) { /* Run broken, only two? */ rpt = 0; /* Yes, do the character twice */ encode(a,-1); /* by calling self recursively. */ if (size <= maxsiz) osize = size; /* Watch out for boundary. */ rpt = 0; /* Call self second time. */ encode(a,-1); return; } else if (rpt > 1) { /* Run broken, more than two? */ xdata[size++] = rptq; /* Yes, emit prefix and count */ xdata[size++] = tochar(++rpt); rpt = 0; /* and reset counter. */ } } a7 = a & 127; /* Get low 7 bits of character */ b8 = a & 128; /* And "parity" bit */ if (ebqflg && b8) { /* If doing 8th bit prefixing */ xdata[size++] = ebq; /* and 8th bit on, insert prefix */ a = a7; /* and clear the 8th bit. */ } if (a7 < 32 || a7 == 127) { /* If in control range */ xdata[size++] = sctlq; /* insert control prefix */ a = ctl(a); /* and make character printable. */ } else if (a7 == sctlq) /* If data is control prefix, */ xdata[size++] = sctlq; /* prefix it. */ else if (ebqflg && a7 == ebq) /* If doing 8th-bit prefixing, */ xdata[size++] = sctlq; /* ditto for 8th-bit prefix. */ else if (rptflg && a7 == rptq) /* If doing run-length encoding, */ xdata[size++] = sctlq; /* ditto for repeat prefix. */ xdata[size++] = a; /* Finally, emit the character. */ xdata[size] = NUL; /* Terminate string with null. */ } int decode(len) int len; { /* Decode packet data field */ unsigned int a, a7, b8; /* Local variables */ rpt = 0; /* Initialize repeat count. */ while (len-- > 0) { a = *rdatap++; /* For each character, a, do... */ if (rptflg) { /* Repeat processing? */ if (a == rptq) { /* Yes, have repeat prefix? */ if (len-- < 1) { if (debug) fprintf(db,"decode rpt error A\n"); return(-1); } rpt = xunchar(*rdatap++); /* Yes, get count */ if (len-- < 1) { if (debug) fprintf(db,"decode rpt error B\n"); return(-1); } a = *rdatap++; /* and the following character. */ } } b8 = 0; /* Assume 8th bit not on. */ if (ebqflg) { /* Doing 8th-bit prefixing? */ if (a == ebq) { /* Yes, have 8th-bit prefix? */ b8 = 128; /* Yes, remember bit 8 on */ if (len-- < 1) { if (debug) fprintf(db,"decode ebq error\n"); return(-1); } a = *rdatap++; /* and get following character. */ } } if (a == rctlq) { /* Control quote? */ if (len-- < 1) { if (debug) fprintf(db,"decode ctl error\n"); return(-1); } a = *rdatap++; /* Yes, get next character */ a7 = a & 127; /* and its low 7 bits */ if (a7 > 62 && a7 < 96) /* Encoded control character? */ a = ctl(a); /* Yes, controllify. */ } if (rpt == 0) /* Rationalize the repeat count. */ rpt = 1; a |= b8; /* OR in the 8th bit. */ a &= 0xff; /* Undo any sign extension. */ /* The following is UNIX-specific but so is this program */ if (text && a == CR) /* Don't output carriage returns. */ continue; for (; rpt > 0; rpt--) { /* Output the character. */ if (osp) { /* As many copies as indicated by */ *osp++ = a; /* the repeat count. */ } else { putc((char)a,ofp); /* Use putc macro here to avoid */ if (ferror(ofp)) { /* function-call overhead. */ if (debug) fprintf(db,"decode putc a=%u errno=%u\n",a,errno); failure = 1; return(-1); } } } } return(0); /* Return successfully when done. */ } VOID spar(s) char *s; { /* Set protocol parameters. */ int k, x; s--; /* Line up with field numbers. */ x = (rln >= 1) ? xunchar(s[1]) : 80;/* Limit on size of inbound packets */ spsiz = (x < 10) ? 80 : x; x = (rln >= 2) ? xunchar(s[2]) : 5; /* Timeout on inbound packets */ if (timint > 0) /* "-b 0" overrides */ timint = (x < 0) ? 5 : x; spadn = 0; spadc = NUL; /* Outbound Padding */ if (rln >= 3) { spadn = xunchar(s[3]); if (rln >= 4) spadc = ctl(s[4]); else spadc = 0; } seol = (rln >= 5) ? xunchar(s[5]) : '\r'; /* Outbound Packet Terminator */ if ((seol < 2) || (seol > 037)) seol = '\r'; x = (rln >= 6) ? s[6] : '#'; /* Control prefix */ rctlq = ((x > 32 && x < 63) || (x > 95 && x < 127)) ? x : '#'; rq = (rln >= 7) ? s[7] : 0; /* 8th-bit prefix */ if (rq == 'Y') rqf = 1; else if ((rq > 32 && rq < 63) || (rq > 95 && rq < 127)) rqf = 2; else rqf = 0; switch (rqf) { case 0: ebqflg = 0; break; case 1: if (parity) { ebqflg = 1; ebq = '&'; } break; case 2: if ((ebqflg = (ebq == sq || sq == 'Y'))) ebq = rq; } x = 1; /* Block check */ if (rln >= 8) { x = s[8] - '0'; if ((x < 1) || (x > 3)) x = 1; } if (bctr > 1 && bctr != x) x = 1; bctr = x; if (rln >= 9) { /* Repeat prefix */ rptq = s[9]; rptflg = ((rptq > 32 && rptq < 63) || (rptq > 95 && rptq < 127)); } else rptflg = 0; k = rln; if (rln >= 10) { /* Capas */ x = xunchar(s[10]); if (x & 8) attributes = 1; if (x & 2) longpackets = 1; for (k = 10; (rln >= k) && (xunchar(s[k]) & 1); k++) ; if (debug) fprintf(db,"spar attributes=%d longpackets=%d\n", attributes, longpackets); } if (rln > k+1) { /* Long packets */ if (longpackets) { x = xunchar(s[k+2]) * 95 + xunchar(s[k+3]); spsiz = (x > MAXSP) ? MAXSP : x; if (spsiz < 10) spsiz = 80; } } #ifndef NOSTREAMING if (rln > k+7) { x = xunchar(s[k+8]); /* Other Kermit's WHATAMI info */ if (debug) fprintf(db,"spar whatami = 0x%x\n",x); if (streamok > -1) if (x & WMI_FLAG) if (x & WMI_STREAM) streamok = 1; if (debug) fprintf(db,"spar streamok = %d\n",streamok); } #endif /* NOSTREAMING */ if (rln > k+8) { /* Other Kermit's system ID */ char buf[16]; int z; x = xunchar(s[k+9]); z = k; k += (9 + x); if (x > 0 && x < 16 && rln >= k) { strncpy(buf,(char *)s+z+10,x); buf[x] = NUL; if (debug) fprintf(db,"spar sysid [%s]\n",buf); if (buf[0] && !manual) { /* If transfer mode is automatic... */ if (!strcmp(buf,"U1")) { /* If UNIX, same as me... */ text = 0; /* Switch to binary mode */ literal = 1; /* and literal filenames */ if (debug) fprintf(db,"spar recognizes peer\n"); } } } } } char * /* Send our protocol parameters */ rpar() { int x; if (rpsiz > 94) xdata[1] = tochar(94); /* Biggest packet I can receive */ else xdata[1] = tochar(rpsiz); xdata[2] = tochar(rtimo); /* When I want to be timed out */ xdata[3] = tochar(rpadn); /* How much padding I need (none) */ xdata[4] = ctl(rpadc); /* Padding character I want */ xdata[5] = tochar(reol); /* End-Of-Line character I want */ xdata[6] = '#'; /* Control-Quote character I send */ switch (rqf) { /* 8th-bit prefix */ case -1: case 1: if (parity) ebq = sq = '&'; break; case 0: case 2: break; } xdata[7] = sq; xdata[8] = bctr + '0'; /* Block check type */ if (rptflg) /* Run length encoding */ xdata[9] = rptq; /* If receiving, agree. */ else xdata[9] = '~'; xdata[10] = tochar(2+8); /* Capabilities (LP+Attributes) */ xdata[11] = tochar(1); /* Window size */ if (urpsiz > 94) { xdata[12] = (char) tochar(urpsiz / 95); /* Long packet size */ xdata[13] = (char) tochar(urpsiz % 95); } xdata[14] = '0'; /* No checkpointing */ xdata[15] = '+'; xdata[16] = '+'; xdata[17] = '+'; x = WMI_FLAG; /* WHATAMI info */ if (literal) x |= WMI_FNAME; /* Filenames literal */ if (!text) x |= WMI_FMODE; /* Text or binary mode */ #ifndef NOSTREAMING if (streamok > -1) /* If streaming not disabled, */ x |= WMI_STREAM; /* offer to stream. */ if (debug) fprintf(db,"rpar streamok=%d whatami=%d\n",streamok,x); #endif /* NOSTREAMING */ xdata[18] = tochar(x); xdata[19] = tochar(2); /* System ID lengh is 2 */ xdata[20] = 'U'; /* UNIX system ID is "U1" */ xdata[21] = '1'; x = WMI2_FLAG; /* WHATAMI2 info */ if (manual) x |= WMI2_XMODE; /* Transfer-mode is manual */ xdata[22] = tochar(x); return(xdata+1); /* Return pointer length field */ } VOID rinit() { /* RECEIVE initialization */ if (quiet) return; tmsgl(versio); tmsgl("Escape back to your local Kermit and give a SEND command."); tmsgl(""); tmsgl("KERMIT READY TO RECEIVE..."); } VOID ginit() { /* GET initialization */ if (quiet) return; tmsgl(versio); tmsgl("Escape back to your local Kermit and give a SERVER command."); tmsgl(""); tmsgl("KERMIT READY TO GET..."); } int gnfile() { /* Get name of next file to send */ if (cz) /* Batch was canceled */ return(0); while (nfils-- > 0) { /* Otherwise continue thru list */ strncpy(filnam,*cmlist++,MAXPATHLEN); filnam[MAXPATHLEN-1] = NUL; if (zchki(filnam) > -1) /* Skip files that can't be read */ return(1); } return(0); /* None left */ } int rcvfil() { /* Receive a file */ char myname[MAXPATHLEN+1]; /* Local name buffer */ cx = 0; /* Reset per-file cancellation flag */ if (cmarg2) { /* User gave as-name? */ if (debug) fprintf(db,"rcvfil as-name [%s]\n",cmarg2); strncpy(myname,cmarg2,MAXPATHLEN); /* Use it */ myname[MAXPATHLEN] = NUL; if (backup) zbackup(myname); /* Handle collisions */ cmarg2 = NULL; /* Undo as-name */ } else { decstr(filnam); /* Decode name string */ if (zrtol(filnam,myname,backup,MAXPATHLEN) < 0) errpkt("Backup file creation failure"); strncpy(filnam,myname,MAXPATHLEN); filnam[MAXPATHLEN] = NUL; if (debug) fprintf(db,"rcvfil filename [%s]\n",filnam); } if (zchko(myname) < 0) /* Check writability */ errpkt("Write access denied"); if (zopeno(myname) < 0) /* Open the output file */ errpkt("Error opening output file"); return(0); } int /* Send a command packet */ #ifdef __STDC__ scmd(char t, char *s) #else scmd(t,s) char t, *s; #endif /* __STDC__ */ { encstr(s); /* Encode the command string */ return(spacket(t,seq,size,xdata)); /* Send the packet */ } gkermit.h000664 045065 024037 00000017611 14144462773 013020 0ustar00fdckermit000000 000000 /* G K E R M I T . H -- GNU Kermit header */ /* Author: Frank da Cruz Originally at: The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA http://www.columbia.edu/kermit/ kermit@columbia.edu As of October 2011: The New Open-Source Kermit Project Bronx NY http://kermitproject.org kermit@kermitproject.org Most recent update: Mon Nov 15 08:24:28 2021 Copyright (C) 1999, 2021 The Trustees of Columbia University in the City of New York. Issued under the GNU General Public License as it existed in 1999. 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. */ #ifndef _GKERMIT_H #define _GKERMIT_H /* __APPLE__ items from Kenji Rikitake 31 May 2021 */ #include #include /* Added in G-Kermit 2.0 */ #ifndef __APPLE__ /* macOS with Xcode has not malloc.h */ #include /* Added in G-Kermit 2.0 */ #endif /* __APPLE__ */ #include /* Added in G-Kermit 2.0 */ #include /* Added in G-Kermit 2.0 */ /* unistd.h might be needed for sleep() prototype */ #ifndef NEEDUNISTD #ifdef __APPLE__ #define NEEDUNISTD #else #ifdef __NetBSD__ #define NEEDUNISTD #endif /* __NetBSD__ */ #endif /* __APPLE__ */ #endif /* NEEDUNISTD */ #ifdef NEEDUNISTD #include #endif /* NEEDUNISTD */ /* Kermit protocol definitions */ #define MAXTRY 10 /* Packet retry limit */ #ifndef SMALL /* Define small systems here */ #ifdef pdp11 /* PDP-11 (64K address space) */ #define SMALL #endif /* pdp11 */ #endif /* SMALL */ #ifdef SMALL /* Buffer sizes for small systems */ #define MAXSP 4000 #define MAXRP 4000 #define DEFRP 1000 #define MAXPATHLEN 255 #define DEVNAMLEN 64 #define MAXRECORD 255 #else /* 32-bit and 64-bit platforms... */ #ifndef MAXSP #define MAXSP 9000 /* Maximum send packet size */ #endif /* MAXSP */ #ifndef MAXRP /* Maximum receive packet size */ #define MAXRP 9000 #endif /* MAXRP */ #ifndef DEFRP /* Default receive packet size */ #define DEFRP 4000 #endif /* DEFRP */ #endif /* SMALL */ #define CTTNAM "/dev/tty" /* Default terminal name */ /* Sizes for file/device-name and file i/o buffers */ #ifndef MAXPATHLEN /* Maximum file specification length */ #define MAXPATHLEN 1024 #endif /* MAXPATHLEN */ #ifndef MAXNAMLEN /* Maximum file name length */ #define MAXNAMLEN 256 #endif /* MAXNAMLEN */ #ifndef DEVNAMLEN /* Maximum device name length */ #define DEVNAMLEN 1024 #endif /* DEVNAMLEN */ #ifndef MAXRECORD /* Text file input buffer length */ #define MAXRECORD 4080 #endif /* MAXRECORD */ #ifdef __STDC__ #define VOID void #else #define VOID int #endif /* __STDC__ */ /* SVORPOSIX = System V or POSIX */ #ifndef SVORPOSIX #ifdef POSIX #define SVORPOSIX #else #ifdef SYSV #define SVORPOSIX #endif /* SYSV */ #endif /* POSIX */ #endif /* SVORPOSIX */ /* Signal type */ #ifndef SIG_V #ifndef SIG_I #ifdef SVORPOSIX #define SIG_V #else #define SIG_I #endif /* SVORPOSIX */ #endif /* SIG_I */ #endif /* SIG_V */ #ifdef SIG_I #define SIGRETURN return(0) #ifndef SIGTYP #define SIGTYP int #endif /* SIGTYP */ #else #ifdef SIG_V #define SIGRETURN return #ifndef SIGTYP #define SIGTYP void #endif /* SIGTYP */ #endif /* SIG_V */ #endif /* SIG_I */ /* WHATAMI bit definitions */ #define WMI_FMODE 2 /* File transfer mode */ #define WMI_FNAME 4 /* File name conversion */ #define WMI_STREAM 8 /* I have a reliable transport */ #define WMI_CLEAR 16 /* I have a clear channel */ #define WMI_FLAG 32 /* Flag that WHATAMI field is valid */ #define WMI2_XMODE 1 /* Transfer mode auto(0)/manual(1) */ #define WMI2_RECU 2 /* Transfer is recursive */ #define WMI2_FLAG 32 /* Flag that WHATAMI2 field is valid */ /* Data types */ #ifndef CHAR #define CHAR unsigned char #endif /* CHAR */ #ifndef ULONG #define ULONG unsigned long #endif /* ULONG */ /* Pointer and character constants */ #ifndef NULL #define NULL 0 /* NULL pointer */ #endif /* NULL */ #define NUL '\000' /* ASCII NUL character */ #define LF '\012' /* ASCII Linefeed */ #define CR '\015' /* ASCII Carriage Return */ #define SP '\040' /* ASCII space character */ #define DEL 127 /* ASCII DEL */ /* Macros */ #define tochar(ch) ((ch) + SP ) /* Number to character */ #define xunchar(ch) ((ch) - SP ) /* Character to number */ #define ctl(ch) ((ch) ^ 64 ) /* Controllify/Uncontrollify */ #define zgetc(a) (((--zincnt)>=0) ? ((int)(*zinptr++) & 0xff) : zfillbuf(a)) /* Function prototype macro works for both ANSI and K&R C */ #ifdef __STDC__ #define _MYPROTOTYPE( func, parms ) func parms #else #define _MYPROTOTYPE( func, parms ) func() #endif /* __STDC__ */ /* Function prototypes */ _MYPROTOTYPE( SIGTYP doexit, (int) ); _MYPROTOTYPE( VOID sysinit, (void) ); _MYPROTOTYPE( char dopar, (char) ); _MYPROTOTYPE( VOID tmsg, (char *) ); _MYPROTOTYPE( VOID tmsgl, (char *) ); _MYPROTOTYPE( int ttopen, (char *) ); _MYPROTOTYPE( int ttpkt, (int) ); _MYPROTOTYPE( int ttres, (void) ); _MYPROTOTYPE( int ttinl, (char *, int, int, char, char, int) ); _MYPROTOTYPE( int ttol, (char *, int) ); _MYPROTOTYPE( int ttchk, (void) ); _MYPROTOTYPE( int ttflui, (void) ); _MYPROTOTYPE( long zchki, (char *) ); _MYPROTOTYPE( int zchko, (char *) ); _MYPROTOTYPE( int zopeni, (char *) ); _MYPROTOTYPE( int zopeno, (char *) ); _MYPROTOTYPE( int zclosi, (void) ); _MYPROTOTYPE( int zcloso, (int) ); _MYPROTOTYPE( int zfillbuf, (int) ); _MYPROTOTYPE( VOID zltor, (char *, char *, int) ); _MYPROTOTYPE( int zrtol, (char *, char *, int, int) ); _MYPROTOTYPE( int zbackup, (char *) ); _MYPROTOTYPE( int input, (void) ); /* Input to state machine (like lex) */ _MYPROTOTYPE( VOID nxtpkt, (void) ); /* Increment packet number */ _MYPROTOTYPE( int ack, (void) ); /* Send empty Acknowledgment */ _MYPROTOTYPE( int ack1, (char *) ); /* Send data-bearing Acknowledgment */ _MYPROTOTYPE( int nak, (void) ); /* Send Negative acknowledgement */ _MYPROTOTYPE( VOID tinit, (void) ); /* Transaction initialization */ _MYPROTOTYPE( VOID errpkt, (char *) ); /* Send error packet */ _MYPROTOTYPE( int sinit, (char) ); /* Send S packet */ _MYPROTOTYPE( int sfile, (void) ); /* Send File header packet */ _MYPROTOTYPE( int sdata, (void) ); /* Send Data packet */ _MYPROTOTYPE( int seof, (char *) ); /* Send EOF packet */ _MYPROTOTYPE( int seot, (void) ); /* Send EOT packet */ _MYPROTOTYPE( int resend, (void) ); /* Resend a packet */ _MYPROTOTYPE( int decode, (int) ); /* Decode packet data field */ _MYPROTOTYPE( int encstr, (char *) ); /* Encode a memory string */ _MYPROTOTYPE( int gattr, (char *) ); /* Get incoming file attributes */ _MYPROTOTYPE( int sattr, (void) ); /* Send file attributes */ _MYPROTOTYPE( VOID ginit, (void) ); /* Transaction initialization */ _MYPROTOTYPE( int scmd, (char, char *) ); /* Send command to Kermit server */ _MYPROTOTYPE( VOID rinit, (void) ); /* Receive initialization */ _MYPROTOTYPE( int gnfile, (void) ); /* Get next filename */ _MYPROTOTYPE( int rcvfil, (void) ); /* Receive incoming file */ _MYPROTOTYPE( VOID spar, (char *) ); /* Set parameters from other Kermit */ _MYPROTOTYPE( char *rpar, (void) ); /* Tell parameters to other Kermit */ _MYPROTOTYPE( VOID usage, (void) ); /* Give usage message */ _MYPROTOTYPE( int gwart, (void) ); /* State table switcher */ /* Externs */ #ifdef ERRNO_H #include #else extern int errno; #endif /* ERRNO_H */ #ifndef _GKERMIT_C extern int debug; #endif /* _GKERMIT_C */ #endif /* _GKERMIT_H */ /* End gkermit.h */ gkermit.nr000664 045065 024037 00000033432 07031702567 013202 0ustar00fdckermit000000 000000 .\" @(#) gkermit.1 1.00 Columbia University 1999/12/25 .TH GKERMIT 1C "25 Dec 1999" "UNIX G-Kermit" .SH NAME gkermit \- G-Kermit (GNU Kermit) 1.00 file transfer software. .ll 80 .SH SYNOPSIS .nf .sp gkermit [ options ] -s file(s) Send files gkermit [ options ] -g file(s) Get files gkermit [ options ] -r Receive files .PP .SH DESCRIPTION G-Kermit is a UNIX program for transferring files using the Kermit protocol. G-Kermit is a product of Kermit Project at Columbia University. It is free software under the GNU Public License. See the COPYING file for details. .PP .SS INVOKING G-KERMIT .PP The G-Kermit binary is called "gkermit". It should be stored someplace in your UNIX PATH; normally it is available as /usr/local/bin/gkermit. To run G-Kermit, just type "gkermit" followed by command-line options that tell it what to do. If no options are given, it prints a usage message listing the available options. .PP If an option takes an argument, the argument is required; if an option does not take an argument, no argument may be given (exception: -d). The action options are -r, -s, and -g. Only one action option may be given. If no action options are given, G-Kermit does nothing (except possibly to print its usage message or create a debug.log file). Here are some examples ("$ " is the shell prompt): .nf .sp $ gkermit -s hello.c <-- Send the hello.c file $ gkermit -s hello.* <-- Send all hello.* files $ gkermit -r <-- Wait to receive files $ gkermit -g hello.c <-- Get hello.c file $ gkermit -g hello.\\* <-- Get all hello.* files .sp .fi Options that do not take arguments can be "bundled" with other options. An option that takes an argument must always be followed by a space and then its argument(s). Examples: .nf .sp $ gkermit -is hello.o <-- Send hello.o in binary mode $ gkermit -dr <-- Receive with debugging .sp .fi .SS COMMAND-LINE OPTIONS .sp .nf -r RECEIVE. Wait for incoming files. -s fn SEND. Send file(s) specified by fn. -g fn GET. Get specified file(s) from server. -a fn AS-NAME. Alternative name for file. -i IMAGE. Binary-mode transfer (default). -T TEXT. Text-mode transfer. -P PATH (filename) conversion disabled. -w WRITEOVER when filenames collide. -K KEEP incompletely received files. -p x PARITY. x = e,o,m,s,n; default = n(one). -e n PACKET LENGTH. n = 40-9000; default=4000. -b n TIMEOUT. Per-packet timeout, seconds. -x XON/XOFF. Set Xon/Xoff in the tty driver. --x Unset Xon/Xoff in the tty driver. -S STREAMING disabled. -X EXTERNAL. G-Kermit is an external protocol. -q QUIET. Suppress messages. -d DEBUG. Write debugging info to ./debug.log. -d fn DEBUG. Write debugging info to given file. -h HELP. Display brief usage message. .fi .PP You may supply options to G-Kermit on the command line or through the GKERMIT environment variable, which can contain any valid gkermit command-line options. These are processed before the actual command-line options and so can be overridden by them. Example for bash or ksh, which you can put in your profile if you want to always keep incomplete files, suppress streaming, suppress messages, and use Space parity: .nf .sp export GKERMIT="-K -S -q -p s" .fi .PP .SS MECHANICS OF FILE TRANSFER .PP To transfer files with G-Kermit you must be connected through a terminal emulator to the UNIX system where G-Kermit is running, meaning you are online to UNIX and have access to the shell prompt (or to a menu that has an option to invoke G-Kermit). The connection can be serial (direct or dialed) or network (Telnet, Rlogin, X.25, etc). .PP When you tell G-Kermit to SEND a file (or files), e.g. with: .nf .sp $ gkermit -Ts oofa.txt .sp .fi it pauses for a second and then sends its first packet. What happens next depends on the capabilities of your terminal emulator: .PP .in +0.5i .ll -0.5i .ta +0.2i .ti -0.2i \(bu If your emulator supports Kermit "autodownloads" then it receives the file automatically and puts you back in the terminal screen when done. .sp .ti -0.2i \(bu Otherwise, you'll need to take whatever action is required by your emulator to get its attention: a mouse action, a keystroke like Alt-x, or a character sequence like Ctrl-\\ or Ctrl-] followed by the letter "c" (this is called "escaping back") and then tell it to receive the file. When the transfer is complete, you must instruct your emulator to go back to its terminal screen. .ll +0.5i .in -0.5i .fi .PP During file transfer, most terminal emulators put up some kind of running display of the file transfer progress. .PP When you tell G-Kermit to RECEIVE (with "gkermit -r"), this requires you to escape back to your terminal emulator and instruct it to send the desired file(s). .PP If your terminal emulator supports Kermit autodownloads AND Kermit server mode, then you can use GET ("gkermit -g files...") rather than RECEIVE ("gkermit -r"), and the rest happens automatically, as when G-Kermit is sending. .SS INTERRUPTING FILE TRANSFER .PP G-Kermit supports file and group interruption. The method for interrupting a transfer depends on your terminal emulator. For example, while the file-transfer display is active, you might type the letter 'x' to cancel the current file and go on to the next one (if any), and the letter 'z' to cancel the group. Or there might be buttons you can click with your mouse. .PP When G-Kermit is in packet mode and your terminal emulator is in its terminal screen, you can also type three (3) Ctrl-C characters in a row to make G-Kermit exit and restore the normal terminal modes. .PP .SS TEXT AND BINARY TRANSFER MODE .PP When sending files in binary mode, G-Kermit sends every byte exactly as it is stored on the disk. This mode is appropriate for program binaries, graphics files, tar archives, compressed files, etc, and is G-Kermit's default file transfer mode when sending. When receiving files in binary mode, G-Kermit simply copies each byte to disk. (Obviously the bytes are encoded for transmission, but the encoding and decoding procedures give a replica of the original file after transfer.) .PP When sending files in text mode, G-Kermit converts the record format to the common one that is defined for the Kermit protocol, namely lines terminated by carriage return and linefeed (CRLF); the receiver converts the CRLFs to whatever line-end or record-format convention is used on its platform. When receiving files in text mode, G-Kermit simply strips carriage returns, leaving only a linefeed at the end of each line, which is the UNIX convention. .PP When receiving files, the sender's transfer mode (text or binary) predominates if the sender gives this information to G-Kermit in a Kermit File Attribute packet, which of course depends on whether your terminal emulator's Kermit protocol has this feature. Otherwise, if you gave a -i or -T option on the gkermit command line, the corresponding mode is used; otherwise the default mode (binary) is used. .PP Furthermore, when either sending or receiving, G-Kermit and your terminal emulator's Kermit can inform each other of their OS type (UNIX in G-Kermit's case). If your emulator supports this capability, which is called "automatic peer recognition", and it tells G-Kermit that its platform is also UNIX, G-Kermit and the emulator's Kermit automatically switch into binary mode, since no record-format conversion is necessary in this case. Automatic peer recognition is disabled automatically if you include the -i (image) or -T (text) option. .PP When sending, G-Kermit sends all files in the same mode, text or binary. There is no automatic per-file mode switching. When receiving, however, per-file switching occurs automatically based on the incoming Attribute packets, if any (explained below), that accompany each file. .PP .SS PATHNAMES .PP When SENDING a file, G-Kermit obtains the filenames from the command line. It depends on the shell to expand metacharacters (wildcards and tilde). .PP G-Kermit uses the full pathname given to find and open the file, but then strips the pathname before sending the name to the receiver. For example: .nf .sp $ gkermit -s /etc/hosts .sp .fi results in the receiver getting a file called "HOSTS" or "hosts" (the directory part, "/etc/", is stripped). .PP However, if a pathname is included in the -a option, the directory part is not stripped: .nf .sp $ gkermit -s /etc/hosts -a /tmp/hosts .sp .fi This example sends the /etc/hosts file but tells the receiver that its name is "/tmp/hosts". What the receiver does with the pathname is, of course, up to the receiver, which might have various options for dealing with incoming pathnames. .PP When RECEIVING a file, G-Kermit does NOT strip the pathname. If the incoming filename includes a path, G-Kermit tries to store the file in the specified place. If the path does not exist, the transfer fails. The incoming pathname can, of course, be overridden with the -a option. .PP .SS FILENAME CONVERSION .PP When sending a file, G-Kermit normally converts outbound filenames to common form: uppercase, no more than one period, and no funny characters. So, for example, gkermit.tar.gz would be sent as GKERMIT_TAR.GZ. .PP When receiving a file, if the name is all uppercase, G-Kermit converts it to all lowercase. If the name contains any lowercase letters, G-Kermit leaves the name alone. .PP If the automatic peer recognition feature is available in the terminal emulator, and G-Kermit recognizes the emulator's platform as UNIX, G-Kermit automatically disables filename conversion and sends and accepts filenames literally. .PP You can force literal filenames by including the -P option on the command line. .PP .SS FILENAME COLLISIONS .PP When G-Kermit receives a file whose name is the same as that of an existing file, G-Kermit "backs up" the existing file by adding a unique suffix to its name. The suffix is ".~n~", where n is a number. This kind of backup suffix is compatible with GNU EMACS and various other popular applications. .PP To defeat the backup feature and have incoming files overwrite existing files of the same name, include the -w (writeover) option on the command line. .PP .SH RETURN VALUES G-Kermit resturns an exit status code of 0 if all actions succeeded and 1 if any actions failed. .PP .SH IMPLEMENTATION NOTES G-Kermit is designed to be small, portable, and stable, and is intended for use only on the "far end" of a connection; it does not make connections itself, although it can be used as an external protocol by other programs that do make connections. To keep it small and stable, it does not include sliding windows, a command or scripting language or character-set translation. To keep it portable and stable, it avoids use of system services that are not standardized across all UNIX varieties and therefore, in particular, does not support file timestamps, internal wildcard expansion, and other features that are not implemented consistently (or at all) across all UNIXes. .PP .SH ENVIRONMENT A GKERMIT environment variable may be defined (for example in your shell profile) to include G-Kermit command-line options; these are processed by G-Kermit before any options that are specified on the command line, and therefore are overriden by command-line options. .PP .SH DIAGNOSTICS If an error occurs during file transfer G-Kermit sends an error packet to your terminal emulator to cancel the transfer; an appropriate error message should be displayed on your screen. .PP .SH ERRORS File transfers can fail for a number of reasons: .sp .in +0.5i .ll -0.5i .ta +0.2i .ti -0.2i \(bu Lack of read access to a source file. .ti -0.2i \(bu Lack of write access to a target directory. .ti -0.2i \(bu Lack of adequate flow control. .ti -0.2i \(bu Use of streaming on an unreliable connection. .ti -0.2i \(bu Excessive unprefixing of control characters. .ti -0.2i \(bu Sending bare 8-bit data on a 7-bit connection. .ti -0.2i \(bu Packets too long for receiver's buffers. .ti -0.2i \(bu Timeout interval too short for connection. .ti -0.2i .ll +0.5i .in -0.5i .fi .sp and many others; these are covered in the references. .PP .SH REFERENCES .PP The Kermit protocol is specified in "Kermit, A File Transfer Protocol" by Frank da Cruz, Digital Press (1987). A correctness proof of the Kermit protocol appears in "Specification and Validation Methods", edited by Egon Boerger, Oxford University Press (1995). "Using C-Kermit" by Frank da Cruz and Christine M. Gianone, Digital Press (1997, or later edition) explains many of the terms and techniques referenced here in case you are not familiar with them, and also includes tutorials on data communications, extensive troubleshooting and performance tips, etc. Various other books on Kermit are available from Digital Press. Online resources include: .nf .sp Web: http://www.columbia.edu/kermit/ FTP: ftp://kermit.columbia.edu/kermit/g/ News: comp.protocols.kermit.misc Email: kermit-support@columbia.edu .fi .sp Also see the README file distributed with G-Kermit for further detail. It can also be found at ftp://kermit.columbia.edu/kermit/g/README. .PP .SH BUGS The speed of a file transfer depends not only on the speed of the two computers involved and the characteristics of the connection, but also on the capabilities and configuration of the two Kermit programs. Kermit is a fast and reliable protocol, but not all implementations of it are necessarily fast or reliable. .PP Nonstreaming transfers on a TCP/IP connection might be inordinately slow if one or both of the TCP/IP stacks uses the Nagle or Delayed ACK tricks. Streaming is used automatically if the other Kermit supports it. .PP When receiving files in text mode, G-Kermit strips all carriage returns, even if they aren't followed by linefeed. .PP A backups files are not guaranteed to have the highest number in their backup suffix. .PP .SH AUTHOR Frank da Cruz, the Kermit Project, Columbia University, New York City, December 1999. .br gproto.c000664 045065 024037 00000026737 14144462775 012676 0ustar00fdckermit000000 000000 /* WARNING -- This C source program generated by gwart preprocessor. */ /* Do not edit this file; edit the gwart-format source file instead, */ /* and then run it through gwart to produce a new C source file. */ /* G P R O T O -- Protocol module for gkermit */ /* -*-C-*- */ /* Author: Frank da Cruz The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA http://www.columbia.edu/kermit/ kermit@columbia.edu Copyright (C) 1999, The Trustees of Columbia University in the City of New York. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "gkermit.h" _MYPROTOTYPE( int closof, (void) ); /* Close output file */ _MYPROTOTYPE( VOID errpkt, (char *) ); /* Send Error packet */ extern char * xdata, *rdatap, **cmlist, *cmarg, *rpar(), strbuf[], filnam[]; extern int start, bctu, bctr, delay, cx, cz, failure, attributes, datalen; extern int streamok, streaming, timint; extern FILE * db; static int x; static VOID streamon() { /* Start streaming if negotiated */ x = 0; if (streamok > 0) { streaming = 1; timint = 0; } } /* Declare gwart states (like lex) */ #define ssini 1 #define ssfil 2 #define ssdat 3 #define ssatt 4 #define sseot 5 #define sseof 6 #define sipkt 7 #define srini 8 #define srfil 9 #define srdat 10 #define sratt 11 /* Packets are read by the input() function, which returns the packet type */ /* that serves as the input to the state machine, which follows... */ #define BEGIN state = int state = 0; int gwart() { int c,actno; extern short tbl[]; while (1) { c = input() - 32; if (c < 0 || c > 95) c = 0; if ((actno = tbl[c + state*96]) != -1) switch(actno) { case 1: { /* Start state for Send. */ tinit(); /* Initialize transaction. */ if (sinit('S') < 0) { errpkt("sinit"); } /* Build and send the S packet. */ else BEGIN ssini; } break; case 2: { /* Receive ACK to I packet */ spar(rdatap); /* Set parameters from it */ bctu = bctr; /* Switch to negotiated block check */ if (gnfile() > 0) { /* Is there a file to send? */ if (sfile() < 0) { /* Yes, open it, send F packet, */ errpkt("sfile"); } else { /* No error */ streamon(); BEGIN ssfil; } } else { /* No files to send, */ if (seot() < 0) { errpkt("seot"); } /* so send EOT packet. */ else BEGIN sseot; } } break; case 3: { /* Receive ACK to File header packet */ if (attributes) { /* If attributes negotiated */ if (sattr() < 0) { /* Send file attributes */ errpkt("sattr"); } else BEGIN ssatt; } else if ((x = sdata()) == 0) { /* Otherwise send first Data packet */ if (seof("") < 0) { /* Empty file - send EOF */ errpkt("seof"); } else { BEGIN sseof; } } else if (x < 0) { /* Error */ errpkt("sdata"); } else { /* OK - switch to Data state */ BEGIN ssdat; } } break; case 4: { /* Receive ACK to Attribute packet */ if (*rdatap == 'N') { /* Check for refusal */ seof("D"); BEGIN sseof; } else if ((x = sdata()) == 0) { /* Otherwise send first Data packet */ if (seof("") < 0) { /* Empty file - send EOF */ errpkt("seof"); } else { BEGIN sseof; } } else if (x < 0) { /* Error */ errpkt("sdata"); } else { /* OK - switch to Data state */ BEGIN ssdat; } } break; case 5: { /* Receive ACK to Data packet */ if (*rdatap == 'X') /* Check for file cancellation */ cx = 1; else if (*rdatap == 'Z') /* Check for batch cancellation */ cz = 1; if ((x = sdata()) == 0) { /* Send data packet if data left. */ if (seof((cx | cz) ? "D" : "") < 0) { /* If not, send Z packet */ errpkt("seof"); } else { BEGIN sseof; } } else if (x < 0) /* Fatal error sending data */ errpkt("sdata"); } break; case 6: { /* Receive ACK to EOF */ if (gnfile() > 0) { /* Get next file from list */ if (sfile() > 0) BEGIN ssfil; else { errpkt("sfile"); } } else { /* No more files */ seot(); /* Send EOT */ BEGIN sseot; } } break; case 7: { return(failure); } break; case 8: { tinit(); rinit(); BEGIN srini; } break; case 9: { /* Receive S packet */ spar(rdatap); /* Set parameters from it */ ack1(rpar()); /* ACK with our parameters */ bctu = bctr; /* Switch to negotiated block check */ streamon(); BEGIN srfil; /* Wait for file or EOT */ } break; case 10: { ack(); return(failure); } break; case 11: { /* Receive File header packet */ if (rcvfil() < 0) { errpkt("rcvfil"); } else { encstr(filnam); ack1(xdata); streamon(); BEGIN sratt; } } break; case 12: { /* Receive Attribute packet */ if (gattr(rdatap) == 0) { ack(); } else { ack1("\""); } } break; case 13: { /* Receive first Data packet */ if (decode(datalen) < 0) { errpkt("Packet decoding error"); } else { ack(); BEGIN srdat; } } break; case 14: { /* Empty file */ if (*rdatap == 'D') /* Check for Discard directive */ cx = 1; if (closof() < 0) { /* Close the output file */ errpkt("closof"); } else { ack(); /* Send ACK */ BEGIN srfil; /* Wait for another file or EOT */ } } break; case 15: { /* Receive Data packet */ if (decode(datalen) < 0) errpkt("Packet decoding error"); else ack(); } break; case 16: { /* Receive EOF packet */ if (*rdatap == 'D') /* Check for Discard directive */ cx = 1; if (closof() < 0) { /* Close the output file */ errpkt("closof"); } else { ack(); /* Send ACK */ BEGIN srfil; /* Wait for another file or EOT */ } } break; case 17: { /* Start state for Get */ tinit(); /* Initialize transaction */ ginit(); /* Initialize Get */ sinit('I'); /* Send I packet */ BEGIN sipkt; } break; case 18: { /* Receive ACK for I packet */ spar(rdatap); /* Set parameters from it */ if (scmd('R',cmarg) < 0) /* Send GET packet file filespec */ errpkt("scmd"); else BEGIN srini; /* Wait for S packet */ } break; case 19: { return(failure = 1); } break; case 20: { errpkt("Unknown packet type"); } break; } } } short tbl[] = { -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 3, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 5, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 4, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 7, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 6, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 18, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 9, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 10, 20, 20, 19, 11, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 15, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 16, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 12, 20, 20, 13, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 14, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 1, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 20,20 }; gproto.w000664 045065 024037 00000014432 07031472422 012672 0ustar00fdckermit000000 000000 /* G P R O T O -- Protocol module for gkermit */ /* -*-C-*- */ /* Author: Frank da Cruz The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA http://www.columbia.edu/kermit/ kermit@columbia.edu Copyright (C) 1999, The Trustees of Columbia University in the City of New York. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "gkermit.h" _MYPROTOTYPE( int closof, (void) ); /* Close output file */ _MYPROTOTYPE( VOID errpkt, (char *) ); /* Send Error packet */ extern char * xdata, *rdatap, **cmlist, *cmarg, *rpar(), strbuf[], filnam[]; extern int start, bctu, bctr, delay, cx, cz, failure, attributes, datalen; extern int streamok, streaming, timint; extern FILE * db; static int x; static VOID streamon() { /* Start streaming if negotiated */ x = 0; if (streamok > 0) { streaming = 1; timint = 0; } } /* Declare gwart states (like lex) */ %states ssini ssfil ssdat ssatt sseot sseof sipkt %states srini srfil srdat sratt /* Packets are read by the input() function, which returns the packet type */ /* that serves as the input to the state machine, which follows... */ %% /* Sending states... */ s { /* Start state for Send. */ tinit(); /* Initialize transaction. */ if (sinit('S') < 0) { errpkt("sinit"); } /* Build and send the S packet. */ else BEGIN ssini; } Y { /* Receive ACK to I packet */ spar(rdatap); /* Set parameters from it */ bctu = bctr; /* Switch to negotiated block check */ if (gnfile() > 0) { /* Is there a file to send? */ if (sfile() < 0) { /* Yes, open it, send F packet, */ errpkt("sfile"); } else { /* No error */ streamon(); BEGIN ssfil; } } else { /* No files to send, */ if (seot() < 0) { errpkt("seot"); } /* so send EOT packet. */ else BEGIN sseot; } } Y { /* Receive ACK to File header packet */ if (attributes) { /* If attributes negotiated */ if (sattr() < 0) { /* Send file attributes */ errpkt("sattr"); } else BEGIN ssatt; } else if ((x = sdata()) == 0) { /* Otherwise send first Data packet */ if (seof("") < 0) { /* Empty file - send EOF */ errpkt("seof"); } else { BEGIN sseof; } } else if (x < 0) { /* Error */ errpkt("sdata"); } else { /* OK - switch to Data state */ BEGIN ssdat; } } Y { /* Receive ACK to Attribute packet */ if (*rdatap == 'N') { /* Check for refusal */ seof("D"); BEGIN sseof; } else if ((x = sdata()) == 0) { /* Otherwise send first Data packet */ if (seof("") < 0) { /* Empty file - send EOF */ errpkt("seof"); } else { BEGIN sseof; } } else if (x < 0) { /* Error */ errpkt("sdata"); } else { /* OK - switch to Data state */ BEGIN ssdat; } } Y { /* Receive ACK to Data packet */ if (*rdatap == 'X') /* Check for file cancellation */ cx = 1; else if (*rdatap == 'Z') /* Check for batch cancellation */ cz = 1; if ((x = sdata()) == 0) { /* Send data packet if data left. */ if (seof((cx | cz) ? "D" : "") < 0) { /* If not, send Z packet */ errpkt("seof"); } else { BEGIN sseof; } } else if (x < 0) /* Fatal error sending data */ errpkt("sdata"); } Y { /* Receive ACK to EOF */ if (gnfile() > 0) { /* Get next file from list */ if (sfile() > 0) BEGIN ssfil; else { errpkt("sfile"); } } else { /* No more files */ seot(); /* Send EOT */ BEGIN sseot; } } Y { return(failure); } /* Send ACK to EOT - done */ /* Receiving states... */ v { tinit(); rinit(); BEGIN srini; } /* Start-state for Receive */ S { /* Receive S packet */ spar(rdatap); /* Set parameters from it */ ack1(rpar()); /* ACK with our parameters */ bctu = bctr; /* Switch to negotiated block check */ streamon(); BEGIN srfil; /* Wait for file or EOT */ } B { ack(); return(failure); } /* Receive EOT packet */ F { /* Receive File header packet */ if (rcvfil() < 0) { errpkt("rcvfil"); } else { encstr(filnam); ack1(xdata); streamon(); BEGIN sratt; } } A { /* Receive Attribute packet */ if (gattr(rdatap) == 0) { ack(); } else { ack1("\""); } } D { /* Receive first Data packet */ if (decode(datalen) < 0) { errpkt("Packet decoding error"); } else { ack(); BEGIN srdat; } } Z { /* Empty file */ if (*rdatap == 'D') /* Check for Discard directive */ cx = 1; if (closof() < 0) { /* Close the output file */ errpkt("closof"); } else { ack(); /* Send ACK */ BEGIN srfil; /* Wait for another file or EOT */ } } D { /* Receive Data packet */ if (decode(datalen) < 0) errpkt("Packet decoding error"); else ack(); } Z { /* Receive EOF packet */ if (*rdatap == 'D') /* Check for Discard directive */ cx = 1; if (closof() < 0) { /* Close the output file */ errpkt("closof"); } else { ack(); /* Send ACK */ BEGIN srfil; /* Wait for another file or EOT */ } } /* GET files from a Kermit server... */ r { /* Start state for Get */ tinit(); /* Initialize transaction */ ginit(); /* Initialize Get */ sinit('I'); /* Send I packet */ BEGIN sipkt; } Y { /* Receive ACK for I packet */ spar(rdatap); /* Set parameters from it */ if (scmd('R',cmarg) < 0) /* Send GET packet file filespec */ errpkt("scmd"); else BEGIN srini; /* Wait for S packet */ } E { return(failure = 1); } /* Receive an Error packet */ . { errpkt("Unknown packet type"); } /* Handle unwanted packet types. */ %% gunixio.c000664 045065 024037 00000072101 14144471641 013020 0ustar00fdckermit000000 000000 /* G U N I X I O -- UNIX i/o module for gkermit */ /* UNIX i/o functions for gkermit. Author: Frank da Cruz Originally at: The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA http://www.columbia.edu/kermit/ kermit@columbia.edu As of October 2011: The New Open-Source Kermit Project Bronx NY http://kermitproject.org kermit@kermitproject.org Last update: Mon Nov 15 09:48:53 2021 Suppress warning about write(1,"\015\012",2); in ttres(). Copyright (C) 1999, 2021 The Trustees of Columbia University in the City of New York. Issued under the GNU General Public License as it existed in 1999. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* CONTENTS... Console Output: tmsg - Type a message tmsgl - Type a line Communication Device: ttopen - Open ttpkt - Put in packet mode ttres - Restore normal mode ttinl - Input a raw packet ttol - Send a packet ttchk - Check if input ready ttflui - Flush input buffer File: zchki - See if file can be opened for input zopeni - Open input file zopeno - Open output file zclosi - Close input file zcloso - Close output file zrtol - Remote-to-Local filename conversion zltor - Local-to-Remote filename conversion zgetc - Get character from input file */ #include /* Standard input/output */ #ifdef POSIX #include /* Terminal modes */ #else #ifdef SYSV #include #else #include #endif /* SYSV */ #endif /* POSIX */ #include /* Character types */ #include /* Needed e.g. by */ #include /* Interrupts */ #include /* Longjumps */ #include /* File exist, file size */ #include /* Error symbols */ #include "gkermit.h" /* gkermit definitions */ /* All versions of HP-UX need Xon/Xoff */ #ifdef hpux /* HP-UX Pre-7.00 */ #ifndef __hpux #define __hpux #endif /* __hpux */ #endif /* hpux */ #ifdef __hpux /* HP-UX 7.00 and later */ #ifndef SETXONXOFF #define SETXONXOFF #endif /* SETXONXOFF */ #endif /* __hpux */ #ifdef NOXONXOFF /* -DNOXONXOFF overrides */ #ifdef SETXONXOFF #undef SETXONXOFF #endif /* SETXONXOFF */ #endif /* NOXONXOFF */ #ifndef TINBUFSIZ /* read() inpbut buffer */ #ifdef USE_GETCHAR #define TINBUFSIZ 0 /* getchar() has its own */ #else #ifdef SMALL #define TINBUFSIZ 240 #else #define TINBUFSIZ 4080 #endif /* SMALL */ #endif /* USE_GETCHAR */ #endif /* TINBUFSIZ */ #ifndef DUMBIO #ifndef USE_GETCHAR #ifndef NOFCNTL_H /* For nonblocking buffered read() */ #ifdef SYS_FCNTL_H #include #else #include #ifndef O_NDELAY #ifdef O_NONBLOCK #define O_NDELAY O_NONBLOCK #endif /* O_NONBLOCK */ #endif /* O_NDELAY */ #endif /* SYS_FCNTL_H */ #endif /* NOFCNTL_H */ #endif /* USE_GETCHAR */ #endif /* DUMBIO */ #ifdef O_NDELAY /* For System V R3 and earlier */ #ifndef EWOULDBLOCK #ifdef EAGAIN #define EWOULDBLOCK EAGAIN #endif /* EAGAIN */ #endif /* EWOULDBLOCK */ #endif /* O_NDELAY */ #ifndef DUMBIO /* To force single-char read/write */ #ifndef USE_GETCHAR #ifndef O_NDELAY #define DUMBIO #endif /* O_NDELAY */ #endif /* USE_GETCHAR */ #endif /* DUMBIO */ /* Header file deficiencies section... */ #ifndef R_OK #define R_OK 4 #endif /* R_OK */ #ifndef W_OK #define W_OK 2 #endif /* W_OK */ #ifndef _IFMT #ifdef S_IFMT #define _IFMT S_IFMT #else #define _IFMT 0170000 #endif /* S_IFMT */ #endif /* _IFMT */ #ifndef S_ISREG #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif /* S_ISREG */ /* External variables */ extern int literal; /* Literal filenames */ extern int quiet; /* No messages */ extern int keep; /* Keep incomplete files */ extern int streamok; /* OK to offer streaming */ extern int nomodes; /* Don't get/set tty modes */ extern int xonxoff; /* Set Xon/Xoff */ extern int noxonxoff; /* Don't set Xon/Xoff */ extern FILE * db; /* Debug log file */ /* Exported variables */ FILE *ifp, *ofp; /* Input and output file pointers */ char zinbuf[MAXRECORD+1]; /* File input buffer */ int zincnt = 0; /* count */ char * zinptr = NULL; /* and pointer. */ /* Private global variables */ static int havemodes = 0; /* Have obtained terminal modes */ static int ttflags = -1; /* Terminal flags */ static int nonblock = 0; /* Nonblocking i/o enabled */ static char tinbuf[TINBUFSIZ+16]; /* Communications input buffer */ static char * tinptr = NULL; /* Pointer to current item */ static int tincnt = 0; /* Buffer count */ static int tlast = 0; /* Last item in buffer */ static int xparity = 0; /* Parity in use, 0 = none */ static int raw = 0; /* Terminal rawmode flag */ static char work[MAXPATHLEN+1]; /* Filename conversion buffer */ /* Terminal mode structs */ #ifdef POSIX /* POSIX */ static struct termios ttold, ttraw; #else #ifdef SYSV /* System V */ static struct termio ttold = {0}; static struct termio ttraw = {0}; #else #ifdef BSD /* 4.2 BSD or UNIX V7 */ static struct sgttyb ttold, ttraw; #endif /* BSD */ #endif /* SYSV */ #endif /* POSIX */ static jmp_buf jbuf; /* Longjump buffer for timeouts */ /* Functions... */ SIGTYP doexit(x) int x; { /* Exit routine */ if (x) /* If failure */ ttflui(); /* flush pending junk we won't read */ ttres(); /* Reset the communication device */ #ifdef F_SETFL if (ttflags > -1) /* Restore its flags */ fcntl(0,F_SETFL,ttflags); #endif /* F_SETFL */ if (debug) { fprintf(db,"exit %d\n",x); fclose(db); } exit(x); } VOID sysinit() { /* To be run first thing */ #ifdef F_SETFL ttflags = fcntl(0,F_GETFL,0); /* Get and save stdin flags */ #endif /* F_SETFL */ #ifdef SIGINT signal(SIGINT,SIG_IGN); /* Ignore interrupts */ #endif /* SIGINT */ #ifdef SIGTSTP signal(SIGTSTP,SIG_IGN); #endif /* SIGTSTP */ #ifdef SIGQUIT signal(SIGQUIT,SIG_IGN); #endif /* SIGQUIT */ signal(SIGHUP,doexit); /* Go here on hangup */ } /* Console Functions */ #ifdef COMMENT /* (not used) */ VOID tmsg(s) char *s; { /* tmsg() */ if (!quiet) fprintf(stderr,"%s",s); /* Type message on the screen. */ } #endif /* COMMENT */ VOID tmsgl(s) char *s; { /* tmsgl() */ if (!quiet) { if (raw) fprintf(stderr,"%s\r\n",s); /* Type message with CRLF */ else fprintf(stderr,"%s\n",s); } } /* Debugging functions */ VOID logerr(s) char * s; { /* Log text and errno */ if (!s) s = ""; if (!debug) return; if (db) fprintf(db,"%s: errno = %d\n",s,errno); } /* Parity function */ char #ifdef __STDC__ dopar(char ch) #else dopar(ch) char ch; #endif /* __STDC__ */ { /* Do parity */ unsigned int a; if (!xparity) return(ch); else ch &= 0177; switch (xparity) { case 'm': return(ch | 128); /* Mark */ case 's': return(ch & 127); /* Space */ case 'o': /* Odd (fall thru) */ case 'e': /* Even */ a = (ch & 15) ^ ((ch >> 4) & 15); a = (a & 3) ^ ((a >> 2) & 3); a = (a & 1) ^ ((a >> 1) & 1); if (xparity == 'o') a = 1 - a; /* Switch sense for odd */ return(ch | (a << 7)); default: return(ch); } } /* Communication functions */ int ttopen(ttname) char *ttname; { /* "Open" the communication device */ if (debug) { /* Vital statistics for debug log */ #ifdef __STDC__ fprintf(db,"ttopen __STDC__\n"); #endif /* __STDC__ */ #ifdef SIG_V fprintf(db,"ttopen SIG_V\n"); #else #ifdef SIG_I fprintf(db,"ttopen SIG_I\n"); #endif /* SIG_I */ #endif /* SIG_V */ #ifdef USE_GETCHAR fprintf(db,"ttopen getchar/putchar\n"); #ifdef BUFSIZ fprintf(db,"ttopen BUFSIZ = %d\n", BUFSIZ); #endif /* BUFSIZ */ #else #ifdef DUMBIO fprintf(db,"ttopen single-byte read/write\n"); #else fprintf(db,"ttopen nonblocking read/write\n"); #endif /* DUMBIO */ #endif /* USE_GETCHAR */ fprintf(db,"ttopen TINBUFSIZ = %d\n", TINBUFSIZ); #ifdef __hpux fprintf(db,"ttopen __hpux\n"); #endif /* __hpux */ #ifdef pdp11 fprintf(db,"ttopen pdp11\n"); #endif /* pdp11 */ #ifdef SETXONXOFF fprintf(db,"ttopen SETXONXOFF\n"); #endif /* SETXONXOFF */ fprintf(db,"ttopen xonxoff = %d\n",xonxoff); fprintf(db,"ttopen noxonxoff = %d\n",noxonxoff); fprintf(db,"ttopen ttflags %d\n",ttflags); fprintf(db,"ttopen nomodes %d\n",nomodes); } if (nomodes) { /* If external protocol */ #ifdef SIGINT /* exit on interrupts */ signal(SIGINT,doexit); #endif /* SIGINT */ #ifdef SIGTSTP signal(SIGTSTP,doexit); #endif /* SIGTSTP */ #ifdef SIGQUIT signal(SIGQUIT,doexit); #endif /* SIGQUIT */ return(0); } #ifndef DUMBIO #ifndef USE_GETCHAR #ifdef O_NDELAY #ifdef F_SETFL if (ttflags != -1) { /* Set nonbocking i/o on stdin */ errno = 0; if (fcntl(0, F_SETFL,ttflags|O_NDELAY) == -1) logerr("ttopen fcntl(0,F_SETFL,O_NDELAY)"); else nonblock = 1; } #endif /* F_SETFL */ #endif /* O_NDELAY */ #endif /* USE_GETCHAR */ #endif /* DUMBIO */ if (!nonblock) /* No streaming without */ streamok = -1; /* nonblocking reads */ if (debug) fprintf(db,"ttopen nonblock = %d\n", nonblock); #ifdef POSIX tcgetattr(0,&ttold); /* Get stdin device attributes */ tcgetattr(0,&ttraw); #else #ifdef SYSV ioctl(0,TCGETA,&ttold); ioctl(0,TCGETA,&ttraw); #else #ifdef BSD gtty(0,&ttold); gtty(0,&ttraw); #endif /* BSD */ #endif /* SYSV */ #endif /* POSIX */ havemodes++; return(0); } int ttpkt(parity) int parity; { /* Put comm device in packet mode */ #ifdef BSD int x; #endif /* BSD */ xparity = parity; /* Make local copy of parity */ if (nomodes) return(0); #ifdef SVORPOSIX /* System V or POSIX section... */ ttraw.c_iflag |= IGNPAR; ttraw.c_lflag &= ~(ICANON|ECHO); ttraw.c_lflag &= ~ISIG; ttraw.c_lflag |= NOFLSH; #ifdef SETXONXOFF if (!noxonxoff) { ttraw.c_iflag |= (IXON|IXOFF); if (debug) fprintf(db,"ttpkt SVORPOSIX Xon/Xoff\n"); } #else if (xonxoff) { if (debug) fprintf(db,"ttpkt SVORPOSIX Xon/Xoff\n"); ttraw.c_iflag |= (IXON|IXOFF); } #endif /* SETXONXOFF */ #ifdef IEXTEN ttraw.c_lflag &= ~IEXTEN; #endif /* IEXTEN */ #ifdef POSIX ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|INPCK|ISTRIP); #else ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|INPCK|ISTRIP|IXANY); #endif /* POSIX */ ttraw.c_oflag &= ~OPOST; ttraw.c_cflag &= ~(CSIZE); ttraw.c_cflag |= (CS8|CREAD|HUPCL); ttraw.c_cflag &= ~(PARENB); #ifndef VEOF ttraw.c_cc[4] = 1; #else #ifdef VMIN ttraw.c_cc[VMIN] = 1; #endif /* VMIN */ #endif /* VEOF */ #ifndef VEOL ttraw.c_cc[5] = 0; #else #ifdef VTIME ttraw.c_cc[VTIME] = 0; #endif /* VTIME */ #endif /* VEOL */ #ifdef VINTR ttraw.c_cc[VINTR] = 0; #endif /* VINTR */ #ifdef POSIX if (tcsetattr(0,TCSADRAIN,&ttraw) < 0) return(-1); #else if (ioctl(0,TCSETAW,&ttraw) < 0) return(-1); #ifdef SYSV #endif /* SYSV */ #endif /* POSIX */ #else /* Not SVORPOSIX */ #ifdef BSD ttraw.sg_flags |= RAW; /* BSD/V7 raw (binary) mode */ #ifdef SETXONXOFF if (!noxonxoff) { ttraw.sg_flags |= TANDEM; if (debug) fprintf(db,"ttpkt BSD Xon/Xoff\n"); } #else if (xonxoff) { ttraw.sg_flags |= TANDEM; if (debug) fprintf(db,"ttpkt BSD Xon/Xoff\n"); } #endif /* SETXONXOFF */ ttraw.sg_flags &= ~(ECHO|CRMOD); /* No echo, etc */ if (stty(0,&ttraw) < 0) return(-1); /* Set modes */ #else system("stty raw -echo"); #endif /* BSD */ #endif /* SVORPOSIX */ raw = 1; /* Flag we're now in raw mode */ return(0); } int ttres() { /* Reset terminal */ int dummy = 0; /* Suppress warning about write() */ int x = 0; /* API return codes */ if (havemodes) { /* Restore old modes */ #ifdef POSIX x = tcsetattr(0,TCSADRAIN,&ttold); #else #ifdef SYSV sleep(1); /* Let output finish */ x = ioctl(0,TCSETAW,&ttold); #else #ifdef BSD sleep(1); /* Let output finish */ x = stty(0,&ttold); #else x = system("stty -raw echo"); #endif /* BSD */ #endif /* SYSV */ #endif /* POSIX */ } dummy = (int) write(1,"\015\012",2); raw = 0; return(x); } int ttchk() { /* Check if input ready */ int x = 0; if (nonblock) { /* Try to read */ errno = 0; x = read(0,&tinbuf[tlast],TINBUFSIZ-tlast); #ifdef EXTRADEBUG fprintf(db,"ttchk read %d errno = %d\n",x,errno); #endif /* EXTRADEBUG */ #ifdef EWOULDBLOCK if (x < 0 && errno == EWOULDBLOCK) /* Nothing to read */ x = 0; #endif /* EWOULDBLOCK */ if (x < 0) /* Fatal i/o error */ return(-1); } tincnt += x; /* Buffer bookkeeping */ tlast += x; return(x + tincnt); /* How much is waiting to be read */ } int ttflui() { /* Flush comm device input buffer */ #ifdef BSD long n = 1; /* Specify read queue */ #endif /* BSD */ int x; tincnt = 0; /* Our own buffer */ tlast = 0; tinptr = tinbuf; errno = 0; #ifdef POSIX x = tcflush(0,TCIFLUSH); /* kernel/driver buffers */ #else #ifdef SYSV x = ioctl(0,TCFLSH,0); #else #ifdef BSD x = ioctl(0,TIOCFLUSH,&n); #endif /* BSD */ #endif /* SYSV */ #endif /* POSIX */ if (debug) fprintf(db,"ttflui = %d, errno = %d\n",x,errno); return(x); } SIGTYP timerh(dummy) int dummy; { /* Timeout handler */ longjmp(jbuf,1); SIGRETURN; } /* ttinl() - Read a raw packet. Call with: dest - where to put it max - maximum length timo - timeout (seconds, 0 = none) eol - packet terminator turn - half-duplex line turnaround character to wait for, 0 = none Returns length obtained, or -1 if error or timeout, -2 on disconnection. */ #ifndef DEBUGWRAP #define DEBUGWRAP 48 #endif /* DEBUGWRAP */ int #ifdef __STDC__ ttinl(char * dest, int max, int timo, char eol, char soh, int turn) #else ttinl(dest,max,timo,eol,soh,turn) int max, timo, turn; char eol, soh, *dest; #endif /* __STDC__ */ { int n = 0, x = 0, flag = 0, rc = 0, ccn = 0; /* Local variables */ char c = NUL; int havelen = 0, pktlen = 0, lplen = 0; #ifdef USE_GETCHAR if (debug) fprintf(db,"ttinl getchar timo = %d\n",timo); #else if (debug) fprintf(db,"ttinl read timo = %d\n",timo); #endif /* USE_GETCHAR */ *dest = NUL; /* Clear destination buffer */ if (timo) { signal(SIGALRM,timerh); /* Enable timer interrupt */ alarm(timo); /* Set it. */ } if (setjmp(jbuf)) { /* Timer went off? */ if (debug) fprintf(db,"ttinl timeout\n"); rc = -1; /* Yes, set this return code. */ } else { /* Otherwise... */ while (1) { /* Read until we have a packet */ #ifdef DUMBIO x = read(0,&c,1); /* Dumb blocking read byte loop */ if (x < 0) { logerr("ttinl XX read 1"); rc = -2; } #else #ifdef USE_GETCHAR errno = 0; x = getchar(); /* Buffered read with getchar() */ if (x == EOF) { if (errno == EINTR) continue; logerr("ttinl getchar"); rc = -2; } c = x; #else /* USE_GETCHAR */ #ifdef O_NDELAY if (nonblock) { /* Buffered nonblocking read() */ int x; if (tincnt < 1) { /* Need to fill our buffer */ errno = 0; tincnt = read(0,tinbuf,TINBUFSIZ); if (tincnt > -1) tlast = tincnt; if (debug) fprintf(db,"ttinl nonblock tincnt=%d errno=%d\n", tincnt,errno); if (tincnt == 0 || errno == EWOULDBLOCK) { #ifdef F_SETFL /* Go back to blocking and wait for 1 char */ if (ttflags != -1) { errno = 0; x = fcntl(0, F_SETFL, ttflags & ~O_NDELAY); if (x == -1 || errno) logerr("ttinl fcntl O_NDELAY off"); errno = 0; tincnt = read(0,tinbuf,1); if (tincnt < 1 || errno) logerr("ttinl BL read"); errno = 0; fcntl(0, F_SETFL, ttflags | O_NDELAY); if (x == -1 || errno) logerr("ttinl fcntl O_NDELAY on"); } if (tincnt == 0) { /* Check results */ continue; } if (tincnt < 0) { /* I/O error */ rc = -2; goto xttinl; } if (debug) fprintf(db,"ttinl blocking read %d\n",tincnt); #else /* No other form of sleeping is portable */ sleep(1); continue; #endif /* F_SETFL */ } else if (tincnt < 0) { rc = -2; goto xttinl; } tinptr = tinbuf; } c = *tinptr++; tincnt--; } else { #endif /* O_NDELAY */ x = read(0,&c,1); /* Dumb read byte loop */ if (x < 0) { logerr("ttinl XX read 1"); rc = -2; } #ifdef O_NDELAY } #endif /* O_NDELAY */ #endif /* USE_GETCHAR */ #endif /* DUMBIO */ if (rc < 0) break; if (xparity) /* Strip parity */ c &= 0x7f; #ifdef COMMENT /* Only uncomment in emergencies */ if (debug) fprintf(db,"ttinl char=%c flag=%d tincnt=%d\n",c,flag,tincnt); #endif /* COMMENT */ if (c == '\03') { /* Got ^C, count it. */ if (++ccn > 2) { /* If more than 2, let them out */ fprintf(stderr,"^C..."); ttres(); if (debug) fprintf(db,"ttinl interrupted\n"); dest[n = 0] = NUL; rc = -9; goto xttinl; } } else /* Not ^C so reset counter*/ ccn = 0; if (!flag && (c != soh)) /* Look for SOH */ continue; /* Skip stuff between packets */ flag = 1; /* Have SOH */ if (n >= max) { if (debug) fprintf(db,"ttinl overflow\n"); rc = -2; goto xttinl; } dest[n++] = c; /* Store the character */ #ifdef USE_EOL /* Use EOL to determine end of packet */ if (c == eol) { dest[n] = NUL; break; } #else /* Use length field for framing */ if (!havelen) { if (n == 2) { pktlen = xunchar(dest[1] & 0x7f); if (pktlen > 1) { if (debug) fprintf(db,"ttinl length = %d\n",pktlen); havelen = 1; } } else if (n == 5 && pktlen == 0) { lplen = xunchar(dest[4] & 0x7f); } else if (n == 6 && pktlen == 0) { pktlen = lplen * 95 + xunchar(dest[5] & 0x7f) + 5; if (debug) fprintf(db,"ttinl length = %d\n",pktlen); havelen = 1; } } if (havelen && (n > pktlen+1)) { if (turn && c != turn) /* Wait for turnaround char */ continue; dest[n] = NUL; /* Null-terminate whatever we got */ break; } #endif /* USE_EOL */ } } xttinl: /* Common exit point */ if (timo) { alarm(0); /* Turn off the alarm */ signal(SIGALRM,SIG_IGN); /* and associated interrupt */ } if (debug && n > 0) { /* Log packet */ #ifndef FULLPACKETS if (n > DEBUGWRAP) { /* Truncate if it would wrap */ dest[n] = NUL; /* in case of interruption */ c = dest[DEBUGWRAP]; dest[DEBUGWRAP] = NUL; fprintf(db,"PKT<-[^A%s...](%d) rc=%d\n",&dest[1],n,rc); dest[DEBUGWRAP] = c; } else #endif /* FULLPACKETS */ fprintf(db,"PKT<-[^A%s](%d) rc=%d\n",&dest[1],n,rc); } if (rc == -9) /* Interrupted by user */ doexit(1); else if (rc > -1) rc = n; return(rc); /* Return length, or failure. */ } int ttol(s,len) int len; char *s; { /* Output string s of given length */ register int i = 0, n = 0, m = 0; int partial = 0; n = len; if (n < 0) { if (debug) fprintf(db,"ttol len = %d\n",n); return(-1); } if (xparity) { /* Add parity if requested */ for (i = 0; i < n; i++) s[i] = dopar(s[i]); } if (debug) { /* Log the packet if requested */ char c; #ifndef FULLPACKETS if (n > DEBUGWRAP) { c = s[DEBUGWRAP]; s[DEBUGWRAP] = NUL; fprintf(db,"PKT->[^A%s...](%d)\n",&s[1],n); s[DEBUGWRAP] = c; } else { #endif /* FULLPACKETS */ c = s[n-1]; s[n-1] = NUL; fprintf(db,"PKT->[^A%s](%d)\n",&s[1],n); s[n-1] = c; #ifndef FULLPACKETS } #endif /* FULLPACKETS */ } #ifdef USE_GETCHAR { /* Send the packet with putchar() */ register CHAR c; register int i; for (i = 0; i < n; i++) { c = *s++; if (putchar(c) == EOF) { logerr("ttol putchar"); return(-1); } } } fflush(stdout); /* Push it out */ return(n); #else while (n > 0) { /* Send the packet with write() */ i = write(1,&s[m],n); /* Allowing for partial results */ if (i < 0) { if (errno == EWOULDBLOCK) /* and even no results at all.. */ continue; logerr("ttol write"); return(-1); } if (i == n) break; partial++; m += i; if (debug) fprintf(db,"ttol partial write %d (%d/%d)\n",i,m,len); n -= i; } if (partial) { m += i; if (debug) fprintf(db,"ttol partial write %d (%d/%d)\n",i,m,len); if (m != len) { if (debug) fprintf(db,"ttol foulup %d != %d\n",m,len); return(-1); } } return(len); #endif /* USE_GETCHAR */ } /* File Functions */ char ofile[MAXPATHLEN]; /* Output filename */ long filelength = -1L; long zchki(fn) char * fn; { /* Check if file is readable */ struct stat buf; if (!fn) return(-1); if (stat(fn,&buf) < 0) return(-1); errno = 0; if (access(fn,R_OK) < 0) { if (debug) fprintf(db,"zchki access %s errno = %d\n",fn,errno); return(-1); } if (!S_ISREG(buf.st_mode)) { if (debug) fprintf(db,"zchki %s is a directory",fn); return(-2); } return(buf.st_size); } int zchko(fn) char *fn; { /* Check write access */ int i, x; char * s; if (!fn) /* Defend against empty name */ fn = ""; if (!*fn) return(-1); if (!strcmp(fn,"/dev/null")) /* Null device is OK. */ return(0); if ((x = zchki(fn)) == -2) /* An existing directory? */ return(-1); s = fn; if (x < 0) { /* If file does not exist */ strncpy(work,fn,MAXPATHLEN); work[MAXPATHLEN] = NUL; s = work; for (i = (int)strlen(s); i > 0; i--) { /* Strip filename from right */ if (s[i-1] == '/') { /* and check its directory */ s[i-1] = NUL; break; } } if (i == 0) s = "."; } errno = 0; x = access(s,W_OK); /* Check access of path. */ if (debug) fprintf(db,"zchko(%s) x = %d errno = %d\n",s,x,errno); return((x < 0) ? -1 : 0); /* and return. */ } int zopeni(name) char *name; { /* Open existing file for input */ ifp = fopen(name,"r"); if (debug) fprintf(db,"zopeni %s: %d\n",name, ifp ? 0 : errno); filelength = zchki(name); if (filelength < 0) return((int)filelength); zincnt = 0; zinptr = zinbuf; return((ifp == NULL) ? -1 : 0); } int zopeno(name) char *name; { /* Open new file for output */ errno = 0; ofp = fopen(name,"w"); if (debug) fprintf(db,"zopeno %s: %d\n",name, ofp ? 0 : errno); if (ofp) { strncpy(ofile,name,MAXPATHLEN); ofile[MAXPATHLEN-1] = NUL; return(0); } else return(-1); } VOID /* Local to remote file name */ zltor(lclnam,pktnam,maxlen) char *lclnam, *pktnam; int maxlen; { char *p, *np = NULL, *cp, *pp, c; char *dotp = NULL; char *dirp = NULL; int n = 0; if (debug) fprintf(db,"zltor %s: maxlen = %d, literal = %d\n", lclnam,maxlen,literal); if (literal) { p = lclnam; dirp = p; while (*p) { if (*p == '/') dirp = p+1; p++; } strncpy(pktnam,dirp,maxlen); } else { for (p = lclnam; *p; p++) { /* Point to name part */ if (*p == '/') np = p; } if (np) { np++; if (!*np) np = lclnam; } else np = lclnam; if (debug) fprintf(db,"zltor np %s\n",np); pp = work; /* Output buffer */ for (cp = np, n = 0; *cp && n < maxlen; cp++,n++) { c = *cp; if (islower(c)) /* Uppercase letters */ *pp++ = toupper(c); /* Change tilde to hyphen */ else if (c == '~') *pp++ = '-'; else if (c == '#') /* Change number sign to 'X' */ *pp++ = 'X'; else if (c == '*' || c == '?') /* Change wildcard chars to 'X' */ *pp++ = 'X'; else if (c == ' ') /* Change space to underscore */ *pp++ = '_'; else if (c < ' ') /* Change space and controls to 'X' */ *pp++ = 'X'; else if (c == '.') { /* Change dot to underscore */ dotp = pp; /* Remember where we last did this */ *pp++ = '_'; } else { if (c == '/') dirp = pp; *pp++ = c; } } *pp = NUL; /* Tie it off. */ if (dotp > dirp) *dotp = '.'; /* Restore last dot in file name */ cp = pktnam; /* If nothing before dot, */ if (*work == '.') *cp++ = 'X'; /* insert 'X' */ strncpy(cp,work,maxlen); cp[maxlen-1] = NUL; } if (debug) fprintf(db,"zltor result: %s\n",pktnam); } int zbackup(fn) char * fn; { /* Back up existing file */ struct stat buf; int i, j, k, x, state, flag; char *p, newname[MAXPATHLEN+12]; if (!fn) /* Watch out for null pointers. */ return(-1); if (!*fn) /* And empty names. */ return(-1); if (stat(fn,&buf) < 0) /* If file doesn't exist */ return(0); /* no need to back it up. */ i = strlen(fn); /* Get length */ if (i > MAXPATHLEN) /* Guard buffer */ i = MAXPATHLEN; if (debug) fprintf(db,"zbackup A %s: %d\n", fn, i); strncpy(work,fn,MAXPATHLEN); /* Make pokeable copy of name */ work[MAXPATHLEN] = NUL; p = work; /* Strip any backup prefix */ i--; for (flag = state = 0; (!flag && (i > 0)); i--) { switch (state) { case 0: /* State 0 - final char */ if (p[i] == '~') /* Is tilde */ state = 1; /* Switch to next state */ else /* Otherwise */ flag = 1; /* Quit - no backup suffix. */ break; case 1: /* State 1 - digits */ if (p[i] == '~' && p[i-1] == '.') { /* Have suffix */ p[i-1] = NUL; /* Trim it */ flag = 1; /* done */ } else if (p[i] >= '0' && p[i] <= '9') { /* In number part */ continue; /* Keep going */ } else { /* Something else */ flag = 1; /* Not a backup suffix - quit. */ } break; } } if (debug) fprintf(db,"zbackup B %s\n", p); if (!p[0]) p = fn; j = strlen(p); strncpy(newname,p,MAXPATHLEN); for (i = 1; i < 1000; i++) { /* Search from 1 to 999 */ if (i < 10) /* Length of numeric part of suffix */ k = 1; else if (i < 100) k = 2; else k = 3; x = j; /* Where to write suffix */ if ((x + k + 3) > MAXPATHLEN) x = MAXPATHLEN - k - 3; sprintf(&newname[x],".~%d~",i); /* Make a backup name */ if (stat(newname,&buf) < 0) { /* If it doesn't exist */ errno = 0; if (link(fn,newname) < 0) { /* Rename old file to backup name */ if (debug) fprintf(db,"zbackup failed: link(%s): %d\n",newname,errno); return(-1); } else if (unlink(fn) < 0) { if (debug) fprintf(db,"zbackup failed: unlink(%s): %d\n",fn,errno); return(-1); } else { if (debug) fprintf(db,"zbackup %s: OK\n",newname); return(0); } } } if (debug) fprintf(db,"zbackup failed: all numbers used\n"); return(-1); } int /* Remote to local filename */ zrtol(pktnam,lclnam,warn,maxlen) char *pktnam, *lclnam; int warn, maxlen; { int acase = 0, flag = 0, n = 0; char * p; if (literal) { strncpy(lclnam,pktnam,maxlen); } else { for (p = lclnam; *pktnam != '\0' && n < maxlen; pktnam++) { if (*pktnam > SP) flag = 1; /* Strip leading blanks and controls */ if (flag == 0 && *pktnam < '!') continue; if (isupper(*pktnam)) /* Check for mixed case */ acase |= 1; else if (islower(*pktnam)) acase |= 2; *p++ = *pktnam; n++; } *p-- = NUL; /* Terminate */ while (*p < '!' && p > lclnam) /* Strip trailing blanks & controls */ *p-- = '\0'; if (!*lclnam) { /* Nothing left? */ strncpy(lclnam,"NONAME",maxlen); /* do this... */ } else if (acase == 1) { /* All uppercase? */ p = lclnam; /* So convert all letters to lower */ while (*p) { if (isupper(*p)) *p = tolower(*p); p++; } } } if (warn) { if (zbackup(lclnam) < 0) return(-1); } return(0); } int zclosi() { /* Close input file */ int rc; rc = (fclose(ifp) == EOF) ? -1 : 0; ifp = NULL; return(rc); } int zcloso(cx) int cx; { /* Close output file */ int rc; rc = (fclose(ofp) == EOF) ? -1 : 0; if (debug) fprintf(db,"zcloso(%s) cx = %d keep = %d\n", ofile, cx, keep); if (cx && !keep ) unlink(ofile); /* Delete if incomplete */ ofp = NULL; return(rc); } int zfillbuf(text) int text; { /* Refill input file buffer */ if (zincnt < 1) { /* Nothing in buffer - must refill */ if (text) { /* Text mode needs LF/CRLF handling */ int c; /* Current character */ for (zincnt = 0; /* Read a line */ zincnt < MAXRECORD - 1 && (c = getc(ifp)) != EOF && c != '\n'; zincnt++ ) { zinbuf[zincnt] = c; } if (c == '\n') { /* Have newline. */ zinbuf[zincnt++] = '\r'; /* Substitute CRLF */ zinbuf[zincnt++] = c; } } else { /* Binary - just read raw buffers */ zincnt = fread(zinbuf, sizeof(char), MAXRECORD, ifp); } zinbuf[zincnt] = NUL; /* Terminate. */ if (zincnt == 0) /* Check for EOF */ return(-1); zinptr = zinbuf; /* Not EOF - reset pointer */ } #ifdef EXTRADEBUG /* Voluminous debugging */ if (debug) fprintf(db,"zfillbuf (%s) zincnt = %d\n", text ? "text" : "binary", zincnt ); #endif /* EXTRADEBUG */ zincnt--; /* Return first byte. */ return(*zinptr++ & 0xff); } gwart.c000664 045065 024037 00000034654 14053714560 012474 0ustar00fdckermit000000 000000 /* G W A R T -- GNU version of Wart A small subset of "lex" sufficient for converting the Kermit protocol state table from lex notation to C. Authors: Jeff Damens, Frank da Cruz The Kermit Project, Columbia University http://www.columbia.edu/kermit/ kermit@columbia.edu Copyright (C) 1984, 1999, The Trustees of Columbia University in the City of New York. Issued under the GNU General Public License as it existed in 1999. 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. */ /* * input format is: * lines to be copied | %state * %% * | CHAR { actions } * ... * %% * more lines to be copied */ #include #include #include "gkermit.h" #define TBL_TYPE "short" /* C data type of state table */ #define C_L 014 /* Formfeed */ #define SEP 1 /* Token types */ #define LBRACK 2 #define RBRACK 3 #define WORD 4 #define COMMA 5 /* Storage sizes */ #define MAXSTATES 50 /* max number of states */ #define MAXWORD 50 /* max # of chars/word */ #define SBYTES ((MAXSTATES+6)/8) /* # of bytes for state bitmask */ /* Name of gwart function in generated program */ #ifndef FNAME #define FNAME "gwart" #endif /* FNAME */ /* Structure for state information */ struct transx { CHAR states[SBYTES]; /* included states */ int anyst; /* true if this good from any state */ CHAR inchr; /* input character */ int actno; /* associated action */ struct transx *nxt; }; /* next transition */ typedef struct transx *trans; /* Function prototypes */ _MYPROTOTYPE( VOID fatal, (char *) ); _MYPROTOTYPE( VOID setwstate, (int, trans) ); _MYPROTOTYPE( int teststate, (int, trans) ); _MYPROTOTYPE( trans rdinput, (FILE *, FILE *) ); _MYPROTOTYPE( VOID initial, (FILE *, FILE *) ); _MYPROTOTYPE( int isin, (char *, int) ); _MYPROTOTYPE( int isword, (int) ); _MYPROTOTYPE( VOID rdword, (FILE *, char *) ); _MYPROTOTYPE( VOID rdstates, (FILE *, FILE *) ); _MYPROTOTYPE( trans newtrans, (void) ); _MYPROTOTYPE( trans rdrules, (FILE *, FILE *) ); _MYPROTOTYPE( VOID statelist, (FILE *, trans) ); _MYPROTOTYPE( VOID copyact, (FILE *, FILE *, int) ); _MYPROTOTYPE( int faction, (trans, int, int) ); _MYPROTOTYPE( VOID emptytbl, (void) ); _MYPROTOTYPE( VOID addaction, (int, int, int) ); _MYPROTOTYPE( VOID writetbl, (FILE *) ); _MYPROTOTYPE( VOID warray, (FILE *, char *, int [], int, char *) ); _MYPROTOTYPE( VOID prolog, (FILE *) ); _MYPROTOTYPE( VOID epilogue, (FILE *) ); _MYPROTOTYPE( VOID copyrest, (FILE *, FILE *) ); _MYPROTOTYPE( int gettoken, (FILE *) ); _MYPROTOTYPE( VOID rdcmnt, (FILE *) ); _MYPROTOTYPE( VOID clrhash, (void) ); _MYPROTOTYPE( int hash, (char *) ); _MYPROTOTYPE( VOID enter, (char *, int) ); _MYPROTOTYPE( int lkup, (char *) ); _MYPROTOTYPE( static char* copy, (char *s) ); /* Variables and tables */ int lines, nstates, nacts; int tbl[MAXSTATES*96]; char tokval[MAXWORD]; char *tbl_type = TBL_TYPE; char *txt1 = "\n#define BEGIN state =\n\nint state = 0;\n\nint\n"; char *fname = FNAME; /* Generated function name goes here */ /* Rest of program... */ char *txt2 = "()\n\ {\n\ int c,actno;\n\ extern "; /* Data type of state table is inserted here (short or int) */ char *txt2a = " tbl[];\n\ while (1) {\n\ c = input() - 32;\n\ if (c < 0 || c > 95) c = 0;\n"; char *txt2b = " if ((actno = tbl[c + state*96]) != -1)\n\ switch(actno) {\n"; /* this program's output goes here, followed by final text... */ char *txt3 = "\n }\n }\n}\n\n"; /* * turn on the bit associated with the given state * */ VOID setwstate(state,t) int state; trans t; { int idx,msk; idx = state/8; /* byte associated with state */ msk = 0x80 >> (state % 8); /* bit mask for state */ t->states[idx] |= msk; } /* * see if the state is involved in the transition * */ int teststate(state,t) int state; trans t; { int idx,msk; idx = state/8; msk = 0x80 >> (state % 8); return(t->states[idx] & msk); } /* * read input from here... * */ trans rdinput(infp,outfp) FILE *infp, *outfp; { trans x; lines = 1; /* line counter */ nstates = 0; /* no states */ nacts = 0; /* no actions yet */ fprintf(outfp,"\n%c* WARNING -- This C source program generated by ",'/'); fprintf(outfp,"gwart preprocessor. */\n"); fprintf(outfp,"%c* Do not edit this file; edit the gwart-format ",'/'); fprintf(outfp,"source file instead, */\n"); fprintf(outfp,"%c* and then run it through gwart to produce a new ",'/'); fprintf(outfp,"C source file. */\n\n"); initial(infp,outfp); /* read state names, initial defs */ prolog(outfp); /* write out our initial code */ x = rdrules(infp,outfp); /* read rules */ epilogue(outfp); /* write out epilogue code */ return(x); } /* * initial - read initial definitions and state names. Returns * on EOF or %%. * */ VOID initial(infp,outfp) FILE *infp, *outfp; { int c; char wordbuf[MAXWORD]; while ((c = getc(infp)) != EOF) { if (c == '%') { rdword(infp,wordbuf); if (strcmp(wordbuf,"states") == 0) rdstates(infp,outfp); else if (strcmp(wordbuf,"%") == 0) return; else fprintf(outfp,"%%%s",wordbuf); } else putc(c,outfp); if (c == '\n') lines++; } } /* * boolean function to tell if the given character can be part of * a word. * */ int isin(s,c) char *s; int c; { for (; *s != '\0'; s++) if (*s == (char) c) return(1); return(0); } int isword(c) int c; { static char special[] = ".%_-$@"; /* these are allowable */ return(isalnum(c) || isin(special,c)); } /* * read the next word into the given buffer. * */ VOID rdword(fp,buf) FILE *fp; char *buf; { int len = 0,c; while (isword(c = getc(fp)) && ++len < MAXWORD) *buf++ = (char) c; *buf++ = '\0'; /* tie off word */ ungetc(c,fp); /* put break char back */ } /* * read state names, up to a newline. * */ VOID rdstates(fp,ofp) FILE *fp,*ofp; { int c; char wordbuf[MAXWORD]; while ((c = getc(fp)) != EOF && c != '\n') { if (isspace(c) || c == C_L) continue; /* skip whitespace */ ungetc(c,fp); /* put char back */ rdword(fp,wordbuf); /* read the whole word */ enter(wordbuf,++nstates); /* put into symbol tbl */ fprintf(ofp,"#define %s %d\n",wordbuf,nstates); } lines++; } /* * allocate a new, empty transition node * */ trans newtrans() { trans new; int i; new = (trans) malloc(sizeof (struct transx)); for (i=0; istates[i] = 0; new->anyst = 0; new->nxt = NULL; return(new); } /* * read all the rules. * */ trans rdrules(fp,out) FILE *fp,*out; { trans head,cur,prev; int curtok; head = cur = prev = NULL; while ((curtok = gettoken(fp)) != SEP) switch(curtok) { case LBRACK: if (cur == NULL) cur = newtrans(); else fatal("duplicate state list"); statelist(fp,cur); /* set states */ continue; /* prepare to read char */ case WORD: if ((int)strlen(tokval) != 1) fatal("multiple chars in state"); if (cur == NULL) { cur = newtrans(); cur->anyst = 1; } cur->actno = ++nacts; cur->inchr = (char) (tokval[0] - 32); if (head == NULL) head = cur; else prev->nxt = cur; prev = cur; cur = NULL; copyact(fp,out,nacts); break; default: fatal("bad input format"); } return(head); } /* * read a list of (comma-separated) states, set them in the * given transition. * */ VOID statelist(fp,t) FILE *fp; trans t; { int curtok,sval; curtok = COMMA; while (curtok != RBRACK) { if (curtok != COMMA) fatal("missing comma"); if ((curtok = gettoken(fp)) != WORD) fatal("missing state name"); if ((sval = lkup(tokval)) == -1) { fprintf(stderr,"state %s undefined\n",tokval); fatal("undefined state"); } setwstate(sval,t); curtok = gettoken(fp); } } /* * copy an action from the input to the output file * */ VOID copyact(inp,outp,actno) FILE *inp,*outp; int actno; { int c,bcnt; fprintf(outp,"case %d:\n",actno); while (c = getc(inp), (isspace(c) || c == C_L)) if (c == '\n') lines++; if (c == '{') { bcnt = 1; fputs(" {",outp); while (bcnt > 0 && (c = getc(inp)) != EOF) { if (c == '{') bcnt++; else if (c == '}') bcnt--; else if (c == '\n') lines++; putc(c,outp); } if (bcnt > 0) fatal("action doesn't end"); } else { while (c != '\n' && c != EOF) { putc(c,outp); c = getc(inp); } lines++; } fprintf(outp,"\n break;\n"); } /* * find the action associated with a given character and state. * returns -1 if one can't be found. * */ int faction(hd,state,chr) trans hd; int state,chr; { while (hd != NULL) { if (hd->anyst || teststate(state,hd)) if (hd->inchr == ('.' - 32) || hd->inchr == (char) chr) return(hd->actno); hd = hd->nxt; } return(-1); } /* * empty the table... * */ VOID emptytbl() { int i; for (i=0; i 1) { if ((infile = fopen(argv[1],"r")) == NULL) { fprintf(stderr,"Can't open %s\n",argv[1]); fatal("unreadable input file"); } } else infile = stdin; if (argc > 2) { if ((outfile = fopen(argv[2],"w")) == NULL) { fprintf(stderr,"Can't write to %s\n",argv[2]); fatal("bad output file"); } } else outfile = stdout; clrhash(); /* empty hash table */ head = rdinput(infile,outfile); /* read input file */ emptytbl(); /* empty our tables */ for (state = 0; state <= nstates; state++) for (c = 1; c < 96; c++) /* find actions, */ addaction(faction(head,state,c),state,c); /* add to tbl */ writetbl(outfile); copyrest(infile,outfile); printf("%d states, %d actions\n",nstates,nacts); exit(0); } /* * fatal error handler * */ VOID fatal(msg) char *msg; { fprintf(stderr,"error in line %d: %s\n",lines,msg); exit(1); } VOID prolog(outfp) FILE *outfp; { int c; while ((c = *txt1++) != '\0') putc(c,outfp); while ((c = *fname++) != '\0') putc(c,outfp); while ((c = *txt2++) != '\0') putc(c,outfp); while ((c = *tbl_type++) != '\0') putc(c,outfp); while ((c = *txt2a++) != '\0') putc(c,outfp); while ((c = *txt2b++) != '\0') putc(c,outfp); } VOID epilogue(outfp) FILE *outfp; { int c; while ((c = *txt3++) != '\0') putc(c,outfp); } VOID copyrest(in,out) FILE *in,*out; { int c; while ((c = getc(in)) != EOF) putc(c,out); } /* * gettoken - returns token type of next token, sets tokval * to the string value of the token if appropriate. * */ int gettoken(fp) FILE *fp; { int c; while (1) { /* loop if reading comments... */ do { c = getc(fp); if (c == '\n') lines++; } while ((isspace(c) || c == C_L)); /* skip whitespace */ switch(c) { case EOF: return(SEP); case '%': if ((c = getc(fp)) == '%') return(SEP); tokval[0] = '%'; tokval[1] = (char) c; rdword(fp,tokval+2); return(WORD); case '<': return(LBRACK); case '>': return(RBRACK); case ',': return(COMMA); case '/': if ((c = getc(fp)) == '*') { rdcmnt(fp); /* skip over the comment */ continue; } else { /* and keep looping */ ungetc(c,fp); /* put this back into input */ c = '/'; /* put character back, fall thru */ } default: if (isword(c)) { ungetc(c,fp); rdword(fp,tokval); return(WORD); } else fatal("Invalid character in input"); } } } /* * skip over a comment * */ VOID rdcmnt(fp) FILE *fp; { int c,star,prcnt; prcnt = star = 0; /* no star seen yet */ while (!((c = getc(fp)) == '/' && star)) { if (c == EOF || (prcnt && c == '%')) fatal("Unterminated comment"); prcnt = (c == '%'); star = (c == '*'); if (c == '\n') lines++; } } /* * symbol table management for gwart * * entry points: * clrhash - empty hash table. * enter - enter a name into the symbol table * lkup - find a name's value in the symbol table. */ #define HASHSIZE 101 /* # of entries in hash table */ struct sym { char *name; /* symbol name */ int val; /* value */ struct sym *hnxt; /* next on collision chain */ } *htab[HASHSIZE]; /* the hash table */ /* * empty the hash table before using it... * */ VOID clrhash() { int i; for (i=0; iname = copy(name); cur->val = svalue; cur->hnxt = htab[h]; htab[h] = cur; } /* * find name in the symbol table, return its value. Returns -1 * if not found. * */ int lkup(name) char *name; { struct sym *cur; for (cur = htab[hash(name)]; cur != NULL; cur = cur->hnxt) if (strcmp(cur->name,name) == 0) return(cur->val); return(-1); } makefile000664 045065 024037 00000006224 07031475166 012700 0ustar00fdckermit000000 000000 # makefile for gkermit - works with make or gmake. # # Author: # Frank da Cruz # The Kermit Project, Columbia University # http://www.columbia.edu/kermit/ # kermit@columbia.edu # December 1999 # # Main build targets: # posix: Build for any POSIX-based platform (default). # sysv: Build for any AT&T UNIX System V based platform. # bsd: Build for any UNIX V7 or 4.3 (or earlier) BSD based platform. # # Special build targets: # sysvx Like sysv but uses getchar()/putchar(). # stty Uses system("stty blah") instead of API calls. # bsd211 For 2.11BSD on the PDP-11 - no nested makes. # # Other targets: # clean: Remove object files # install: Install gkermit # uninstall: Uninstall gkermit # # Default compiler is cc. To force gcc use: # make "CC=gcc" [ ] # # See README and COPYING for further information. # Sample installation values - change or override as needed. BINDIR = /usr/local/bin MANDIR = /usr/man/manl TEXTDIR = /usr/local/doc INFODIR = /usr/local/info MANEXT = l # Default compiler and flags CC=cc CFLAGS= -DPOSIX -O $(KFLAGS) # Object files OBJECTS= gproto.o gkermit.o gunixio.o gcmdline.o # Targets and dependencies all: gwart gkermit gwart.o: gwart.c $(CC) $(CFLAGS) -c gwart.c gwart: gwart.o $(CC) -o gwart gwart.o .c.o: $(CC) $(CFLAGS) -c $< gproto.c: gproto.w gkermit.h ./gwart gproto.w gproto.c gkermit.o: gkermit.c gkermit.h gunixio.o: gunixio.c gkermit.h gcmdline.o: gcmdline.c gkermit.h gkermit: gproto.o gkermit.o gunixio.o gcmdline.o $(CC) -o gkermit $(OBJECTS) bsd: gwart $(MAKE) "CC=$(CC)" "CFLAGS=-DBSD -O $(KFLAGS)" gkermit sysv: gwart $(MAKE) "CC=$(CC)" "CFLAGS=-DSYSV -O $(KFLAGS)" gkermit posix: gwart $(MAKE) "CC=$(CC)" "CFLAGS=-DPOSIX -O $(KFLAGS)" gkermit sysvx: gwart $(MAKE) "CC=$(CC)" \ "CFLAGS=-DSYSV -DUSE_GETCHAR -O $(KFLAGS)" gkermit stty: gwart $(MAKE) "CC=$(CC)" "CFLAGS=$(KFLAGS)" gkermit bsd211: gwart ./gwart gproto.w gproto.c cc -DBSD $(KFLAGS) -c gkermit.c cc -DBSD $(KFLAGS) -c gproto.c cc -DBSD $(KFLAGS) -c gcmdline.c cc -DBSD $(KFLAGS) -c gunixio.c cc -o gkermit $(OBJECTS) clean: rm -f $(OBJECTS) gproto.o gproto.c gwart.o gwart install: @if test -f ./gkermit; then \ echo "Installing gkermit..." ; \ else \ echo "Please build the gkermit binary first." ; \ exit ; \ fi @echo Copying gkermit to $(BINDIR)... @cp gkermit $(BINDIR)/gkermit @chmod 755 $(BINDIR)/gkermit @ls -lg $(BINDIR)/gkermit @if test -d $(TEXTDIR); then \ echo "$(TEXTDIR) exists..." ; \ else \ echo "Creating $(TEXTDIR)/..." ; \ mkdir $(TEXTDIR) ; \ chmod 755 $(TEXTDIR) ; \ fi @echo Copying README to $(TEXTDIR)/gkermit.txt... @cp README $(TEXTDIR)/gkermit.txt @chmod 644 $(TEXTDIR)/gkermit.txt @ls -lg $(TEXTDIR)/gkermit.txt @echo Installing man page in $(MANDIR)/gkermit.$(MANEXT)... @cp gkermit.nr $(MANDIR)/gkermit.$(MANEXT) @chmod 644 $(MANDIR)/gkermit.$(MANEXT) @ls -lg $(MANDIR)/gkermit.$(MANEXT) uninstall: @echo Uninstalling gkermit... rm -f $(BINDIR)/gkermit \ $(TEXTDIR)/gkermit.txt \ $(MANDIR)gkermit.$(MANEXT) .PHONY: clean install uninstall # (end)